r194: Applied Bernard Jungen's patch, with a few modifications:
[rox-filer.git] / ROX-Filer / src / action.c
blob79cc81477561543e0739f22d952e3099294535e9
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 static gboolean is_sub_dir(char *sub, char *parent)
253 int parent_len;
255 parent_len = strlen(parent);
256 if (strncmp(parent, sub, parent_len))
257 return FALSE;
259 /* sub is at least as long as parent and all characters upto
260 * parent's length match.
263 return sub[parent_len] == 0 || sub[parent_len] == '/';
266 static gboolean display_dir(gpointer data)
268 GUIside *gui_side = (GUIside *) data;
270 gtk_label_set_text(GTK_LABEL(gui_side->dir), gui_side->next_dir + 1);
271 g_free(gui_side->next_dir);
272 gui_side->next_dir = NULL;
274 return FALSE;
277 /* Called when the child sends us a message */
278 static void message_from_child(gpointer data,
279 gint source,
280 GdkInputCondition condition)
282 char buf[5];
283 GUIside *gui_side = (GUIside *) data;
284 GtkWidget *log = gui_side->log;
286 if (read_exact(source, buf, 4))
288 ssize_t message_len;
289 char *buffer;
291 buf[4] = '\0';
292 message_len = strtol(buf, NULL, 16);
293 buffer = g_malloc(message_len + 1);
294 if (message_len > 0 && read_exact(source, buffer, message_len))
296 buffer[message_len] = '\0';
297 if (*buffer == '?')
298 SENSITIVE_YESNO(gui_side, TRUE);
299 else if (*buffer == '+')
301 refresh_dirs(buffer + 1);
302 g_free(buffer);
303 return;
305 else if (*buffer == 'm')
307 filer_check_mounted(buffer + 1);
308 g_free(buffer);
309 return;
311 else if (*buffer == '/')
313 if (gui_side->next_dir)
314 g_free(gui_side->next_dir);
315 else
316 gui_side->next_timer =
317 gtk_timeout_add(500,
318 display_dir,
319 gui_side);
320 gui_side->next_dir = buffer;
321 return;
323 else if (*buffer == '!')
324 gui_side->errors++;
326 gtk_text_insert(GTK_TEXT(log),
327 NULL,
328 *buffer == '!' ? &red : NULL,
329 NULL,
330 buffer + 1, message_len - 1);
331 g_free(buffer);
332 return;
334 g_print("Child died in the middle of a message.\n");
337 /* The child is dead */
338 gui_side->child = 0;
340 fclose(gui_side->to_child);
341 gui_side->to_child = NULL;
342 close(gui_side->from_child);
343 gdk_input_remove(gui_side->input_tag);
344 gtk_widget_set_sensitive(gui_side->quiet, FALSE);
346 if (gui_side->errors)
348 GString *report;
349 report = g_string_new(NULL);
350 g_string_sprintf(report, "There %s %d error%s.\n",
351 gui_side->errors == 1 ? "was" : "were",
352 gui_side->errors,
353 gui_side->errors == 1 ? "" : "s");
354 gtk_text_insert(GTK_TEXT(log), NULL, &red, NULL,
355 report->str, report->len);
357 g_string_free(report, TRUE);
359 else if (gui_side->show_info == FALSE)
360 gtk_widget_destroy(gui_side->window);
363 /* Scans src_dir, updating dest_path whenever cb returns TRUE */
364 static void for_dir_contents(ForDirCB *cb, char *src_dir, char *dest_path)
366 DIR *d;
367 struct dirent *ent;
368 GSList *list = NULL, *next;
370 d = opendir(src_dir);
371 if (!d)
373 send_error();
374 return;
377 send_dir(src_dir);
379 while ((ent = readdir(d)))
381 if (ent->d_name[0] == '.' && (ent->d_name[1] == '\0'
382 || (ent->d_name[1] == '.' && ent->d_name[2] == '\0')))
383 continue;
384 list = g_slist_append(list, g_strdup(make_path(src_dir,
385 ent->d_name)->str));
387 closedir(d);
389 if (!list)
390 return;
392 next = list;
394 while (next)
396 if (cb((char *) next->data, dest_path))
398 g_string_sprintf(message, "+%s", dest_path);
399 send();
402 g_free(next->data);
403 next = next->next;
405 g_slist_free(list);
406 return;
409 /* Read this many bytes into the buffer. TRUE on success. */
410 static gboolean read_exact(int source, char *buffer, ssize_t len)
412 while (len > 0)
414 ssize_t got;
415 got = read(source, buffer, len);
416 if (got < 1)
417 return FALSE;
418 len -= got;
419 buffer += got;
421 return TRUE;
424 /* Send 'message' to our parent process. TRUE on success. */
425 static gboolean send()
427 char len_buffer[5];
428 ssize_t len;
430 g_return_val_if_fail(message->len < 0xffff, FALSE);
432 sprintf(len_buffer, "%04x", message->len);
433 fwrite(len_buffer, 1, 4, to_parent);
434 len = fwrite(message->str, 1, message->len, to_parent);
435 fflush(to_parent);
436 return len == message->len;
439 /* Set the directory indicator at the top of the window */
440 static gboolean send_dir(char *dir)
442 g_string_sprintf(message, "/%s", dir);
443 return send();
446 static gboolean send_error()
448 g_string_sprintf(message, "!ERROR: %s\n", g_strerror(errno));
449 return send();
452 static void button_reply(GtkWidget *button, GUIside *gui_side)
454 char *text;
456 if (!gui_side->to_child)
457 return;
459 text = gtk_object_get_data(GTK_OBJECT(button), "send-code");
460 g_return_if_fail(text != NULL);
461 fputc(*text, gui_side->to_child);
462 fflush(gui_side->to_child);
464 if (*text == 'Y' || *text == 'N' || *text == 'Q')
465 SENSITIVE_YESNO(gui_side, FALSE);
468 static void process_flag(char flag)
470 switch (flag)
472 case 'Q':
473 quiet = !quiet;
474 break;
475 case 'F':
476 o_force = !o_force;
477 break;
478 default:
479 g_string_sprintf(message,
480 "!ERROR: Bad message '%c'\n", flag);
481 send();
482 break;
486 /* If the parent has sent any flag toggles, read them */
487 static void check_flags(void)
489 fd_set set;
490 int got;
491 char retval;
492 struct timeval tv;
494 FD_ZERO(&set);
496 while (1)
498 FD_SET(from_parent, &set);
499 tv.tv_sec = 0;
500 tv.tv_usec = 0;
501 got = select(from_parent + 1, &set, NULL, NULL, &tv);
503 if (got == -1)
504 g_error("select() failed: %s\n", g_strerror(errno));
505 else if (!got)
506 return;
508 got = read(from_parent, &retval, 1);
509 if (got != 1)
510 g_error("read() error: %s\n", g_strerror(errno));
512 process_flag(retval);
516 /* Read until the user sends a reply. If ignore_quiet is TRUE then
517 * the user MUST click Yes or No, else treat quiet on as Yes.
518 * If the user needs prompting then does send().
520 static gboolean reply(int fd, gboolean ignore_quiet)
522 ssize_t len;
523 char retval;
524 gboolean asked = FALSE;
526 while (ignore_quiet || !quiet)
528 if (!asked)
530 send();
531 asked = TRUE;
534 len = read(fd, &retval, 1);
535 if (len != 1)
537 fprintf(stderr, "read() error: %s\n",
538 g_strerror(errno));
539 _exit(1); /* Parent died? */
542 switch (retval)
544 case 'Q':
545 quiet = !quiet;
546 if (ignore_quiet)
548 g_string_assign(message, "?");
549 send();
551 break;
552 case 'Y':
553 g_string_assign(message, "' Yes\n");
554 send();
555 return TRUE;
556 case 'N':
557 g_string_assign(message, "' No\n");
558 send();
559 return FALSE;
560 default:
561 process_flag(retval);
562 break;
566 if (asked)
568 g_string_assign(message, "' Quiet\n");
569 send();
571 return TRUE;
574 static void destroy_action_window(GtkWidget *widget, gpointer data)
576 GUIside *gui_side = (GUIside *) data;
578 if (gui_side->child)
580 kill(gui_side->child, SIGTERM);
581 fclose(gui_side->to_child);
582 close(gui_side->from_child);
583 gdk_input_remove(gui_side->input_tag);
586 if (gui_side->next_dir)
588 gtk_timeout_remove(gui_side->next_timer);
589 g_free(gui_side->next_dir);
591 g_free(gui_side);
593 if (--number_of_windows < 1)
594 gtk_main_quit();
597 /* Create two pipes, fork() a child and return a pointer to a GUIside struct
598 * (NULL on failure). The child calls func().
600 * If autoq then automatically selects 'Quiet'.
602 static GUIside *start_action(gpointer data, ActionChild *func, gboolean autoq)
604 int filedes[4]; /* 0 and 2 are for reading */
605 GUIside *gui_side;
606 int child;
607 GtkWidget *vbox, *button, *hbox, *scrollbar, *actions;
608 struct sigaction act;
610 if (pipe(filedes))
612 report_error("ROX-Filer", g_strerror(errno));
613 return NULL;
616 if (pipe(filedes + 2))
618 close(filedes[0]);
619 close(filedes[1]);
620 report_error("ROX-Filer", g_strerror(errno));
621 return NULL;
624 child = fork();
625 switch (child)
627 case -1:
628 report_error("ROX-Filer", g_strerror(errno));
629 return NULL;
630 case 0:
631 /* We are the child */
633 quiet = autoq;
635 /* Reset the SIGCHLD handler */
636 act.sa_handler = SIG_DFL;
637 sigemptyset(&act.sa_mask);
638 act.sa_flags = 0;
639 sigaction(SIGCHLD, &act, NULL);
641 message = g_string_new(NULL);
642 close(filedes[0]);
643 close(filedes[3]);
644 to_parent = fdopen(filedes[1], "wb");
645 from_parent = filedes[2];
646 func(data);
647 _exit(0);
650 /* We are the parent */
651 close(filedes[1]);
652 close(filedes[2]);
653 gui_side = g_malloc(sizeof(GUIside));
654 gui_side->from_child = filedes[0];
655 gui_side->to_child = fdopen(filedes[3], "wb");
656 gui_side->log = NULL;
657 gui_side->child = child;
658 gui_side->errors = 0;
659 gui_side->show_info = FALSE;
661 gui_side->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
662 gtk_container_set_border_width(GTK_CONTAINER(gui_side->window), 2);
663 gtk_window_set_default_size(GTK_WINDOW(gui_side->window), 450, 200);
664 gtk_signal_connect(GTK_OBJECT(gui_side->window), "destroy",
665 GTK_SIGNAL_FUNC(destroy_action_window), gui_side);
667 gui_side->vbox = vbox = gtk_vbox_new(FALSE, 4);
668 gtk_container_add(GTK_CONTAINER(gui_side->window), vbox);
670 gui_side->dir = gtk_label_new("<dir>");
671 gui_side->next_dir = NULL;
672 gtk_misc_set_alignment(GTK_MISC(gui_side->dir), 0.5, 0.5);
673 gtk_box_pack_start(GTK_BOX(vbox), gui_side->dir, FALSE, TRUE, 0);
675 hbox = gtk_hbox_new(FALSE, 0);
676 gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 0);
678 gui_side->log = gtk_text_new(NULL, NULL);
679 gtk_widget_set_usize(gui_side->log, 400, 100);
680 gtk_box_pack_start(GTK_BOX(hbox), gui_side->log, TRUE, TRUE, 0);
681 scrollbar = gtk_vscrollbar_new(GTK_TEXT(gui_side->log)->vadj);
682 gtk_box_pack_start(GTK_BOX(hbox), scrollbar, FALSE, TRUE, 0);
684 actions = gtk_hbox_new(TRUE, 4);
685 gtk_box_pack_start(GTK_BOX(vbox), actions, FALSE, TRUE, 0);
687 gui_side->quiet = button = gtk_toggle_button_new_with_label("Quiet");
688 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), autoq);
689 gtk_object_set_data(GTK_OBJECT(button), "send-code", "Q");
690 gtk_box_pack_start(GTK_BOX(actions), button, TRUE, TRUE, 0);
691 gtk_signal_connect(GTK_OBJECT(button), "clicked",
692 button_reply, gui_side);
693 gui_side->yes = button = gtk_button_new_with_label("Yes");
694 gtk_object_set_data(GTK_OBJECT(button), "send-code", "Y");
695 gtk_box_pack_start(GTK_BOX(actions), button, TRUE, TRUE, 0);
696 gtk_signal_connect(GTK_OBJECT(button), "clicked",
697 button_reply, gui_side);
698 gui_side->no = button = gtk_button_new_with_label("No");
699 gtk_object_set_data(GTK_OBJECT(button), "send-code", "N");
700 gtk_box_pack_start(GTK_BOX(actions), button, TRUE, TRUE, 0);
701 gtk_signal_connect(GTK_OBJECT(button), "clicked",
702 button_reply, gui_side);
703 SENSITIVE_YESNO(gui_side, FALSE);
705 button = gtk_button_new_with_label("Abort");
706 gtk_box_pack_start(GTK_BOX(actions), button, TRUE, TRUE, 0);
707 gtk_signal_connect_object(GTK_OBJECT(button), "clicked",
708 gtk_widget_destroy, GTK_OBJECT(gui_side->window));
710 gui_side->input_tag = gdk_input_add(gui_side->from_child,
711 GDK_INPUT_READ,
712 message_from_child,
713 gui_side);
715 return gui_side;
718 /* ACTIONS ON ONE ITEM */
720 /* These may call themselves recursively, or ask questions, etc.
721 * TRUE iff the directory containing dest_path needs to be rescanned.
724 /* dest_path is the dir containing src_path.
725 * Updates the global size_tally.
727 static gboolean do_usage(char *src_path, char *dest_path)
729 struct stat info;
731 check_flags();
733 if (lstat(src_path, &info))
735 g_string_sprintf(message, "'%s:\n", src_path);
736 send();
737 send_error();
738 return FALSE;
741 if (S_ISREG(info.st_mode) || S_ISLNK(info.st_mode))
742 size_tally += info.st_size;
743 else if (S_ISDIR(info.st_mode))
745 g_string_sprintf(message, "?Count contents of %s?",
746 src_path);
747 if (reply(from_parent, FALSE))
749 char *safe_path;
750 safe_path = g_strdup(src_path);
751 for_dir_contents(do_usage, safe_path, safe_path);
752 g_free(safe_path);
756 return FALSE;
759 /* dest_path is the dir containing src_path */
760 static gboolean do_delete(char *src_path, char *dest_path)
762 struct stat info;
763 gboolean write_prot;
765 check_flags();
767 if (lstat(src_path, &info))
769 send_error();
770 return FALSE;
773 write_prot = S_ISLNK(info.st_mode) ? FALSE
774 : access(src_path, W_OK) != 0;
775 if (write_prot || !quiet)
777 g_string_sprintf(message, "?Delete %s'%s'?",
778 write_prot ? "WRITE-PROTECTED " : " ",
779 src_path);
780 if (!reply(from_parent, write_prot && !o_force))
781 return FALSE;
783 else
785 g_string_sprintf(message, "'Removing '%s'\n", src_path);
786 send();
789 if (S_ISDIR(info.st_mode))
791 char *safe_path;
792 safe_path = g_strdup(src_path);
793 for_dir_contents(do_delete, safe_path, safe_path);
794 if (rmdir(safe_path))
796 g_free(safe_path);
797 send_error();
798 return FALSE;
800 g_string_assign(message, "'Directory deleted\n");
801 send();
802 g_free(safe_path);
804 else if (unlink(src_path))
806 send_error();
807 return FALSE;
810 return TRUE;
813 static gboolean do_copy2(char *path, char *dest)
815 char *dest_path;
816 char *leaf;
817 struct stat info;
818 struct stat dest_info;
819 gboolean retval = TRUE;
821 check_flags();
823 leaf = strrchr(path, '/');
824 if (!leaf)
825 leaf = path; /* Error? */
826 else
827 leaf++;
829 dest_path = make_path(dest, leaf)->str;
831 if (lstat(path, &info))
833 send_error();
834 return FALSE;
837 if (lstat(dest_path, &dest_info) == 0)
839 int err;
840 gboolean merge;
842 merge = S_ISDIR(info.st_mode) && S_ISDIR(dest_info.st_mode);
844 g_string_sprintf(message, "?'%s' already exists - %s?",
845 dest_path,
846 merge ? "merge contents" : "overwrite");
848 if (!reply(from_parent, TRUE))
849 return FALSE;
851 if (!merge)
853 if (S_ISDIR(dest_info.st_mode))
854 err = rmdir(dest_path);
855 else
856 err = unlink(dest_path);
858 if (err)
860 send_error();
861 if (errno != ENOENT)
862 return FALSE;
863 g_string_sprintf(message,
864 "'Trying copy anyway...\n");
865 send();
869 else if (!quiet)
871 g_string_sprintf(message, "?Copy %s as %s?", path, dest_path);
872 if (!reply(from_parent, FALSE))
873 return FALSE;
875 else
877 g_string_sprintf(message, "'Copying %s as %s\n", path,
878 dest_path);
879 send();
882 if (S_ISDIR(info.st_mode))
884 char *safe_path, *safe_dest;
885 struct stat dest_info;
886 gboolean exists;
888 /* (we will do the update ourselves now, rather than
889 * afterwards)
891 retval = FALSE;
893 safe_path = g_strdup(path);
894 safe_dest = g_strdup(dest_path);
896 exists = !lstat(dest_path, &dest_info);
898 if (exists && !S_ISDIR(dest_info.st_mode))
900 g_string_sprintf(message,
901 "!ERROR: Destination already exists, "
902 "but is not a directory\n");
904 else if (exists == FALSE && mkdir(dest_path, info.st_mode))
905 send_error();
906 else
908 if (!exists)
910 /* (just been created then) */
911 g_string_sprintf(message, "+%s", dest);
912 send();
915 for_dir_contents(do_copy2, safe_path, safe_dest);
916 /* Note: dest_path now invalid... */
919 g_free(safe_path);
920 g_free(safe_dest);
922 else if (S_ISLNK(info.st_mode))
924 char target[MAXPATHLEN + 1];
925 int count;
927 /* Not all versions of cp(1) can make symlinks,
928 * so we special-case it.
931 count = readlink(path, target, sizeof(target) - 1);
932 if (count < 0)
934 send_error();
935 retval = FALSE;
937 else
939 target[count] = '\0';
940 if (symlink(target, dest_path))
942 send_error();
943 retval = FALSE;
947 else
949 char *argv[] = {"cp", "-pRf", NULL, NULL, NULL};
951 argv[2] = path;
952 argv[3] = dest_path;
954 if (fork_exec_wait(argv))
956 g_string_sprintf(message, "!ERROR: Copy failed\n");
957 send();
958 retval = FALSE;
962 return retval;
965 /* Copy path to dest.
966 * Check that path not copied into itself.
967 * path and dest are real paths (coming from the filer)
969 static gboolean do_copy(char *path, char *dest)
971 if (is_sub_dir(dest, path))
973 g_string_sprintf(message,
974 "!ERROR: Can't copy directory into itself\n");
975 send();
976 return FALSE;
978 return do_copy2(path, dest);
981 /* Move path to dest.
982 * Check that path not moved into itself.
983 * path and dest are real paths (coming from the filer)
985 static gboolean do_move(char *path, char *dest)
987 char *dest_path;
988 char *leaf;
989 gboolean retval = TRUE;
990 char *argv[] = {"mv", "-f", NULL, NULL, NULL};
992 if (is_sub_dir(dest, path))
994 g_string_sprintf(message,
995 "!ERROR: Can't move directory into itself\n");
996 send();
997 return FALSE;
1000 check_flags();
1002 leaf = strrchr(path, '/');
1003 if (!leaf)
1004 leaf = path; /* Error? */
1005 else
1006 leaf++;
1008 dest_path = make_path(dest, leaf)->str;
1010 if (access(dest_path, F_OK) == 0)
1012 struct stat info;
1013 int err;
1015 g_string_sprintf(message, "?'%s' already exists - overwrite?",
1016 dest_path);
1017 if (!reply(from_parent, TRUE))
1018 return FALSE;
1020 if (lstat(dest_path, &info))
1022 send_error();
1023 return FALSE;
1026 if (S_ISDIR(info.st_mode))
1027 err = rmdir(dest_path);
1028 else
1029 err = unlink(dest_path);
1031 if (err)
1033 send_error();
1034 if (errno != ENOENT)
1035 return FALSE;
1036 g_string_sprintf(message,
1037 "'Trying move anyway...\n");
1038 send();
1041 else if (!quiet)
1043 g_string_sprintf(message, "?Move %s as %s?", path, dest_path);
1044 if (!reply(from_parent, FALSE))
1045 return FALSE;
1047 else
1049 g_string_sprintf(message, "'Moving %s as %s\n", path,
1050 dest_path);
1051 send();
1054 argv[2] = path;
1055 argv[3] = dest;
1057 if (fork_exec_wait(argv) == 0)
1059 g_string_sprintf(message, "+%s", path);
1060 g_string_truncate(message, leaf - path);
1061 send();
1063 else
1065 g_string_sprintf(message, "!ERROR: Failed to move %s as %s\n",
1066 path, dest_path);
1067 retval = FALSE;
1069 send();
1071 return retval;
1074 static gboolean do_link(char *path, char *dest)
1076 char *dest_path;
1077 char *leaf;
1079 check_flags();
1081 leaf = strrchr(path, '/');
1082 if (!leaf)
1083 leaf = path; /* Error? */
1084 else
1085 leaf++;
1087 dest_path = make_path(dest, leaf)->str;
1089 if (quiet)
1091 g_string_sprintf(message, "'Linking %s as %s\n", path,
1092 dest_path);
1093 send();
1095 else
1097 g_string_sprintf(message, "?Link %s as %s?", path, dest_path);
1098 if (!reply(from_parent, FALSE))
1099 return FALSE;
1102 if (symlink(path, dest_path))
1104 send_error();
1105 return FALSE;
1108 return TRUE;
1111 /* Mount/umount this item */
1112 static void do_mount(FilerWindow *filer_window, DirItem *item)
1114 char *argv[3] = {NULL, NULL, NULL};
1116 check_flags();
1118 argv[0] = item->flags & ITEM_FLAG_MOUNTED ? "umount" : "mount";
1119 argv[1] = make_path(filer_window->path, item->leafname)->str;
1121 if (quiet)
1123 g_string_sprintf(message, "'%sing %s\n", argv[0], argv[1]);
1124 send();
1126 else
1128 g_string_sprintf(message, "?%s %s?", argv[0], argv[1]);
1129 if (!reply(from_parent, FALSE))
1130 return;
1133 if (fork_exec_wait(argv) == 0)
1135 g_string_sprintf(message, "+%s", filer_window->path);
1136 send();
1137 g_string_sprintf(message, "m%s", argv[1]);
1138 send();
1140 else
1142 g_string_sprintf(message, "!ERROR: %s failed\n", argv[0]);
1143 send();
1147 /* CHILD MAIN LOOPS */
1149 /* After forking, the child calls one of these functions */
1151 static void usage_cb(gpointer data)
1153 FilerWindow *filer_window = (FilerWindow *) data;
1154 Collection *collection = filer_window->collection;
1155 DirItem *item;
1156 int left = collection->number_selected;
1157 int i = -1;
1158 off_t total_size = 0;
1160 send_dir(filer_window->path);
1162 while (left > 0)
1164 i++;
1165 if (!collection->items[i].selected)
1166 continue;
1167 item = (DirItem *) collection->items[i].data;
1168 size_tally = 0;
1169 do_usage(make_path(filer_window->path,
1170 item->leafname)->str,
1171 filer_window->path);
1172 g_string_sprintf(message, "'%s: %s\n",
1173 item->leafname,
1174 format_size((unsigned long) size_tally));
1175 send();
1176 total_size += size_tally;
1177 left--;
1180 g_string_sprintf(message, "'\nTotal: %s\n",
1181 format_size((unsigned long) total_size));
1182 send();
1185 #ifdef DO_MOUNT_POINTS
1186 static void mount_cb(gpointer data)
1188 FilerWindow *filer_window = (FilerWindow *) data;
1189 Collection *collection = filer_window->collection;
1190 DirItem *item;
1191 int i;
1192 gboolean mount_points = FALSE;
1194 send_dir(filer_window->path);
1196 if (mount_item)
1197 do_mount(filer_window, mount_item);
1198 else
1200 for (i = 0; i < collection->number_of_items; i++)
1202 if (!collection->items[i].selected)
1203 continue;
1204 item = (DirItem *) collection->items[i].data;
1205 if (!(item->flags & ITEM_FLAG_MOUNT_POINT))
1206 continue;
1207 mount_points = TRUE;
1209 do_mount(filer_window, item);
1212 if (!mount_points)
1214 g_string_sprintf(message,
1215 "!No mount points selected!\n");
1216 send();
1220 g_string_sprintf(message, "'\nDone\n");
1221 send();
1223 #endif
1225 static void delete_cb(gpointer data)
1227 FilerWindow *filer_window = (FilerWindow *) data;
1228 Collection *collection = filer_window->collection;
1229 DirItem *item;
1230 int left = collection->number_selected;
1231 int i = -1;
1233 send_dir(filer_window->path);
1235 while (left > 0)
1237 i++;
1238 if (!collection->items[i].selected)
1239 continue;
1240 item = (DirItem *) collection->items[i].data;
1241 if (do_delete(make_path(filer_window->path,
1242 item->leafname)->str,
1243 filer_window->path))
1245 g_string_sprintf(message, "+%s", filer_window->path);
1246 send();
1248 left--;
1251 g_string_sprintf(message, "'\nDone\n");
1252 send();
1255 static void list_cb(gpointer data)
1257 GSList *paths = (GSList *) data;
1259 while (paths)
1261 send_dir((char *) paths->data);
1263 if (action_do_func((char *) paths->data, action_dest))
1265 g_string_sprintf(message, "+%s", action_dest);
1266 send();
1269 paths = paths->next;
1272 g_string_sprintf(message, "'\nDone\n");
1273 send();
1276 static void add_toggle(GUIside *gui_side, guchar *label, guchar *code)
1278 GtkWidget *check;
1280 check = gtk_check_button_new_with_label(label);
1281 gtk_object_set_data(GTK_OBJECT(check), "send-code", code);
1282 gtk_signal_connect(GTK_OBJECT(check), "clicked",
1283 button_reply, gui_side);
1284 gtk_box_pack_start(GTK_BOX(gui_side->vbox), check, FALSE, TRUE, 0);
1288 /* EXTERNAL INTERFACE */
1290 /* Count disk space used by selected items */
1291 void action_usage(FilerWindow *filer_window)
1293 GUIside *gui_side;
1294 Collection *collection;
1296 collection = filer_window->collection;
1298 if (collection->number_selected < 1)
1300 report_error("ROX-Filer", "You need to select some items "
1301 "to count");
1302 return;
1305 gui_side = start_action(filer_window, usage_cb, TRUE);
1306 if (!gui_side)
1307 return;
1309 gui_side->show_info = TRUE;
1311 gtk_window_set_title(GTK_WINDOW(gui_side->window), "Disk Usage");
1312 number_of_windows++;
1313 gtk_widget_show_all(gui_side->window);
1316 /* Mount/unmount 'item', or all selected mount points if NULL. */
1317 void action_mount(FilerWindow *filer_window, DirItem *item)
1319 #ifdef DO_MOUNT_POINTS
1320 GUIside *gui_side;
1321 Collection *collection;
1323 collection = filer_window->collection;
1325 if (item == NULL && collection->number_selected < 1)
1327 report_error("ROX-Filer", "You need to select some items "
1328 "to mount or unmount");
1329 return;
1332 mount_item = item;
1333 gui_side = start_action(filer_window, mount_cb, o_auto_mount);
1334 if (!gui_side)
1335 return;
1337 gtk_window_set_title(GTK_WINDOW(gui_side->window), "Mount / Unmount");
1338 number_of_windows++;
1339 gtk_widget_show_all(gui_side->window);
1340 #else
1341 report_error("ROX-Filer",
1342 "ROX-Filer does not yet support mount points on your "
1343 "system. Sorry.");
1344 #endif /* DO_MOUNT_POINTS */
1347 /* Deletes all selected items in the window */
1348 void action_delete(FilerWindow *filer_window)
1350 GUIside *gui_side;
1351 Collection *collection;
1353 collection = filer_window->collection;
1355 if (collection->number_selected < 1)
1357 report_error("ROX-Filer", "You need to select some items "
1358 "to delete");
1359 return;
1362 gui_side = start_action(filer_window, delete_cb, o_auto_delete);
1363 if (!gui_side)
1364 return;
1366 gtk_window_set_title(GTK_WINDOW(gui_side->window), "Delete");
1367 add_toggle(gui_side,
1368 "Don't confirm deletion of non-writeable items", "F");
1370 number_of_windows++;
1371 gtk_widget_show_all(gui_side->window);
1374 void action_copy(GSList *paths, char *dest)
1376 GUIside *gui_side;
1378 action_dest = dest;
1379 action_do_func = do_copy;
1380 gui_side = start_action(paths, list_cb, o_auto_copy);
1381 if (!gui_side)
1382 return;
1384 gtk_window_set_title(GTK_WINDOW(gui_side->window), "Copy");
1385 number_of_windows++;
1386 gtk_widget_show_all(gui_side->window);
1389 void action_move(GSList *paths, char *dest)
1391 GUIside *gui_side;
1393 action_dest = dest;
1394 action_do_func = do_move;
1395 gui_side = start_action(paths, list_cb, o_auto_move);
1396 if (!gui_side)
1397 return;
1399 gtk_window_set_title(GTK_WINDOW(gui_side->window), "Move");
1400 number_of_windows++;
1401 gtk_widget_show_all(gui_side->window);
1404 void action_link(GSList *paths, char *dest)
1406 GUIside *gui_side;
1408 action_dest = dest;
1409 action_do_func = do_link;
1410 gui_side = start_action(paths, list_cb, o_auto_link);
1411 if (!gui_side)
1412 return;
1414 gtk_window_set_title(GTK_WINDOW(gui_side->window), "Link");
1415 number_of_windows++;
1416 gtk_widget_show_all(gui_side->window);
1419 void action_init(void)
1421 options_sections = g_slist_prepend(options_sections, &options);
1422 option_register("action_auto_quiet", action_auto_quiet);