r189: Effective permissions are shown in brackets.
[rox-filer/ma.git] / ROX-Filer / src / action.c
blob2a0a86381eedbf6cb36f0961765bec26bfa21d6f
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 static gboolean display_dir(gpointer data)
252 GUIside *gui_side = (GUIside *) data;
254 gtk_label_set_text(GTK_LABEL(gui_side->dir), gui_side->next_dir + 1);
255 g_free(gui_side->next_dir);
256 gui_side->next_dir = NULL;
258 return FALSE;
261 /* Called when the child sends us a message */
262 static void message_from_child(gpointer data,
263 gint source,
264 GdkInputCondition condition)
266 char buf[5];
267 GUIside *gui_side = (GUIside *) data;
268 GtkWidget *log = gui_side->log;
270 if (read_exact(source, buf, 4))
272 ssize_t message_len;
273 char *buffer;
275 buf[4] = '\0';
276 message_len = strtol(buf, NULL, 16);
277 buffer = g_malloc(message_len + 1);
278 if (message_len > 0 && read_exact(source, buffer, message_len))
280 buffer[message_len] = '\0';
281 if (*buffer == '?')
282 SENSITIVE_YESNO(gui_side, TRUE);
283 else if (*buffer == '+')
285 refresh_dirs(buffer + 1);
286 g_free(buffer);
287 return;
289 else if (*buffer == 'm')
291 filer_check_mounted(buffer + 1);
292 g_free(buffer);
293 return;
295 else if (*buffer == '/')
297 if (gui_side->next_dir)
298 g_free(gui_side->next_dir);
299 else
300 gui_side->next_timer =
301 gtk_timeout_add(500,
302 display_dir,
303 gui_side);
304 gui_side->next_dir = buffer;
305 return;
307 else if (*buffer == '!')
308 gui_side->errors++;
310 gtk_text_insert(GTK_TEXT(log),
311 NULL,
312 *buffer == '!' ? &red : NULL,
313 NULL,
314 buffer + 1, message_len - 1);
315 g_free(buffer);
316 return;
318 g_print("Child died in the middle of a message.\n");
321 /* The child is dead */
322 gui_side->child = 0;
324 fclose(gui_side->to_child);
325 gui_side->to_child = NULL;
326 close(gui_side->from_child);
327 gdk_input_remove(gui_side->input_tag);
328 gtk_widget_set_sensitive(gui_side->quiet, FALSE);
330 if (gui_side->errors)
332 GString *report;
333 report = g_string_new(NULL);
334 g_string_sprintf(report, "There %s %d error%s.\n",
335 gui_side->errors == 1 ? "was" : "were",
336 gui_side->errors,
337 gui_side->errors == 1 ? "" : "s");
338 gtk_text_insert(GTK_TEXT(log), NULL, &red, NULL,
339 report->str, report->len);
341 g_string_free(report, TRUE);
343 else if (gui_side->show_info == FALSE)
344 gtk_widget_destroy(gui_side->window);
347 /* Scans src_dir, updating dest_path whenever cb returns TRUE */
348 static void for_dir_contents(ForDirCB *cb, char *src_dir, char *dest_path)
350 DIR *d;
351 struct dirent *ent;
352 GSList *list = NULL, *next;
354 d = opendir(src_dir);
355 if (!d)
357 send_error();
358 return;
361 send_dir(src_dir);
363 while ((ent = readdir(d)))
365 if (ent->d_name[0] == '.' && (ent->d_name[1] == '\0'
366 || (ent->d_name[1] == '.' && ent->d_name[2] == '\0')))
367 continue;
368 list = g_slist_append(list, g_strdup(make_path(src_dir,
369 ent->d_name)->str));
371 closedir(d);
373 if (!list)
374 return;
376 next = list;
378 while (next)
380 if (cb((char *) next->data, dest_path))
382 g_string_sprintf(message, "+%s", dest_path);
383 send();
386 g_free(next->data);
387 next = next->next;
389 g_slist_free(list);
390 return;
393 /* Read this many bytes into the buffer. TRUE on success. */
394 static gboolean read_exact(int source, char *buffer, ssize_t len)
396 while (len > 0)
398 ssize_t got;
399 got = read(source, buffer, len);
400 if (got < 1)
401 return FALSE;
402 len -= got;
403 buffer += got;
405 return TRUE;
408 /* Send 'message' to our parent process. TRUE on success. */
409 static gboolean send()
411 char len_buffer[5];
412 ssize_t len;
414 g_return_val_if_fail(message->len < 0xffff, FALSE);
416 sprintf(len_buffer, "%04x", message->len);
417 fwrite(len_buffer, 1, 4, to_parent);
418 len = fwrite(message->str, 1, message->len, to_parent);
419 fflush(to_parent);
420 return len == message->len;
423 /* Set the directory indicator at the top of the window */
424 static gboolean send_dir(char *dir)
426 g_string_sprintf(message, "/%s", dir);
427 return send();
430 static gboolean send_error()
432 g_string_sprintf(message, "!ERROR: %s\n", g_strerror(errno));
433 return send();
436 static void button_reply(GtkWidget *button, GUIside *gui_side)
438 char *text;
440 if (!gui_side->to_child)
441 return;
443 text = gtk_object_get_data(GTK_OBJECT(button), "send-code");
444 g_return_if_fail(text != NULL);
445 fputc(*text, gui_side->to_child);
446 fflush(gui_side->to_child);
448 if (*text == 'Y' || *text == 'N' || *text == 'Q')
449 SENSITIVE_YESNO(gui_side, FALSE);
452 static void process_flag(char flag)
454 switch (flag)
456 case 'Q':
457 quiet = !quiet;
458 break;
459 case 'F':
460 o_force = !o_force;
461 break;
462 default:
463 g_string_sprintf(message,
464 "!ERROR: Bad message '%c'\n", flag);
465 send();
466 break;
470 /* If the parent has sent any flag toggles, read them */
471 static void check_flags(void)
473 fd_set set;
474 int got;
475 char retval;
476 struct timeval tv;
478 FD_ZERO(&set);
480 while (1)
482 FD_SET(from_parent, &set);
483 tv.tv_sec = 0;
484 tv.tv_usec = 0;
485 got = select(from_parent + 1, &set, NULL, NULL, &tv);
487 if (got == -1)
488 g_error("select() failed: %s\n", g_strerror(errno));
489 else if (!got)
490 return;
492 got = read(from_parent, &retval, 1);
493 if (got != 1)
494 g_error("read() error: %s\n", g_strerror(errno));
496 process_flag(retval);
500 /* Read until the user sends a reply. If ignore_quiet is TRUE then
501 * the user MUST click Yes or No, else treat quiet on as Yes.
502 * If the user needs prompting then does send().
504 static gboolean reply(int fd, gboolean ignore_quiet)
506 ssize_t len;
507 char retval;
508 gboolean asked = FALSE;
510 while (ignore_quiet || !quiet)
512 if (!asked)
514 send();
515 asked = TRUE;
518 len = read(fd, &retval, 1);
519 if (len != 1)
521 fprintf(stderr, "read() error: %s\n",
522 g_strerror(errno));
523 _exit(1); /* Parent died? */
526 switch (retval)
528 case 'Q':
529 quiet = !quiet;
530 if (ignore_quiet)
532 g_string_assign(message, "?");
533 send();
535 break;
536 case 'Y':
537 g_string_assign(message, "' Yes\n");
538 send();
539 return TRUE;
540 case 'N':
541 g_string_assign(message, "' No\n");
542 send();
543 return FALSE;
544 default:
545 process_flag(retval);
546 break;
550 if (asked)
552 g_string_assign(message, "' Quiet\n");
553 send();
555 return TRUE;
558 static void destroy_action_window(GtkWidget *widget, gpointer data)
560 GUIside *gui_side = (GUIside *) data;
562 if (gui_side->child)
564 kill(gui_side->child, SIGTERM);
565 fclose(gui_side->to_child);
566 close(gui_side->from_child);
567 gdk_input_remove(gui_side->input_tag);
570 if (gui_side->next_dir)
572 gtk_timeout_remove(gui_side->next_timer);
573 g_free(gui_side->next_dir);
575 g_free(gui_side);
577 if (--number_of_windows < 1)
578 gtk_main_quit();
581 /* Create two pipes, fork() a child and return a pointer to a GUIside struct
582 * (NULL on failure). The child calls func().
584 * If autoq then automatically selects 'Quiet'.
586 static GUIside *start_action(gpointer data, ActionChild *func, gboolean autoq)
588 int filedes[4]; /* 0 and 2 are for reading */
589 GUIside *gui_side;
590 int child;
591 GtkWidget *vbox, *button, *hbox, *scrollbar, *actions;
592 struct sigaction act;
594 if (pipe(filedes))
596 report_error("ROX-Filer", g_strerror(errno));
597 return NULL;
600 if (pipe(filedes + 2))
602 close(filedes[0]);
603 close(filedes[1]);
604 report_error("ROX-Filer", g_strerror(errno));
605 return NULL;
608 child = fork();
609 switch (child)
611 case -1:
612 report_error("ROX-Filer", g_strerror(errno));
613 return NULL;
614 case 0:
615 /* We are the child */
617 quiet = autoq;
619 /* Reset the SIGCHLD handler */
620 act.sa_handler = SIG_DFL;
621 sigemptyset(&act.sa_mask);
622 act.sa_flags = 0;
623 sigaction(SIGCHLD, &act, NULL);
625 message = g_string_new(NULL);
626 close(filedes[0]);
627 close(filedes[3]);
628 to_parent = fdopen(filedes[1], "wb");
629 from_parent = filedes[2];
630 func(data);
631 _exit(0);
634 /* We are the parent */
635 close(filedes[1]);
636 close(filedes[2]);
637 gui_side = g_malloc(sizeof(GUIside));
638 gui_side->from_child = filedes[0];
639 gui_side->to_child = fdopen(filedes[3], "wb");
640 gui_side->log = NULL;
641 gui_side->child = child;
642 gui_side->errors = 0;
643 gui_side->show_info = FALSE;
645 gui_side->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
646 gtk_container_set_border_width(GTK_CONTAINER(gui_side->window), 2);
647 gtk_window_set_default_size(GTK_WINDOW(gui_side->window), 450, 200);
648 gtk_signal_connect(GTK_OBJECT(gui_side->window), "destroy",
649 GTK_SIGNAL_FUNC(destroy_action_window), gui_side);
651 gui_side->vbox = vbox = gtk_vbox_new(FALSE, 4);
652 gtk_container_add(GTK_CONTAINER(gui_side->window), vbox);
654 gui_side->dir = gtk_label_new("<dir>");
655 gui_side->next_dir = NULL;
656 gtk_misc_set_alignment(GTK_MISC(gui_side->dir), 0.5, 0.5);
657 gtk_box_pack_start(GTK_BOX(vbox), gui_side->dir, FALSE, TRUE, 0);
659 hbox = gtk_hbox_new(FALSE, 0);
660 gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 0);
662 gui_side->log = gtk_text_new(NULL, NULL);
663 gtk_widget_set_usize(gui_side->log, 400, 100);
664 gtk_box_pack_start(GTK_BOX(hbox), gui_side->log, TRUE, TRUE, 0);
665 scrollbar = gtk_vscrollbar_new(GTK_TEXT(gui_side->log)->vadj);
666 gtk_box_pack_start(GTK_BOX(hbox), scrollbar, FALSE, TRUE, 0);
668 actions = gtk_hbox_new(TRUE, 4);
669 gtk_box_pack_start(GTK_BOX(vbox), actions, FALSE, TRUE, 0);
671 gui_side->quiet = button = gtk_toggle_button_new_with_label("Quiet");
672 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), autoq);
673 gtk_object_set_data(GTK_OBJECT(button), "send-code", "Q");
674 gtk_box_pack_start(GTK_BOX(actions), button, TRUE, TRUE, 0);
675 gtk_signal_connect(GTK_OBJECT(button), "clicked",
676 button_reply, gui_side);
677 gui_side->yes = button = gtk_button_new_with_label("Yes");
678 gtk_object_set_data(GTK_OBJECT(button), "send-code", "Y");
679 gtk_box_pack_start(GTK_BOX(actions), button, TRUE, TRUE, 0);
680 gtk_signal_connect(GTK_OBJECT(button), "clicked",
681 button_reply, gui_side);
682 gui_side->no = button = gtk_button_new_with_label("No");
683 gtk_object_set_data(GTK_OBJECT(button), "send-code", "N");
684 gtk_box_pack_start(GTK_BOX(actions), button, TRUE, TRUE, 0);
685 gtk_signal_connect(GTK_OBJECT(button), "clicked",
686 button_reply, gui_side);
687 SENSITIVE_YESNO(gui_side, FALSE);
689 button = gtk_button_new_with_label("Abort");
690 gtk_box_pack_start(GTK_BOX(actions), button, TRUE, TRUE, 0);
691 gtk_signal_connect_object(GTK_OBJECT(button), "clicked",
692 gtk_widget_destroy, GTK_OBJECT(gui_side->window));
694 gui_side->input_tag = gdk_input_add(gui_side->from_child,
695 GDK_INPUT_READ,
696 message_from_child,
697 gui_side);
699 return gui_side;
702 /* ACTIONS ON ONE ITEM */
704 /* These may call themselves recursively, or ask questions, etc.
705 * TRUE iff the directory containing dest_path needs to be rescanned.
708 /* dest_path is the dir containing src_path.
709 * Updates the global size_tally.
711 static gboolean do_usage(char *src_path, char *dest_path)
713 struct stat info;
715 check_flags();
717 if (lstat(src_path, &info))
719 g_string_sprintf(message, "'%s:\n", src_path);
720 send();
721 send_error();
722 return FALSE;
725 if (S_ISREG(info.st_mode) || S_ISLNK(info.st_mode))
726 size_tally += info.st_size;
727 else if (S_ISDIR(info.st_mode))
729 g_string_sprintf(message, "?Count contents of %s?",
730 src_path);
731 if (reply(from_parent, FALSE))
733 char *safe_path;
734 safe_path = g_strdup(src_path);
735 for_dir_contents(do_usage, safe_path, safe_path);
736 g_free(safe_path);
740 return FALSE;
743 /* dest_path is the dir containing src_path */
744 static gboolean do_delete(char *src_path, char *dest_path)
746 struct stat info;
747 gboolean write_prot;
749 check_flags();
751 if (lstat(src_path, &info))
753 send_error();
754 return FALSE;
757 write_prot = S_ISLNK(info.st_mode) ? FALSE
758 : access(src_path, W_OK) != 0;
759 g_string_sprintf(message, "?Delete %s'%s'?",
760 write_prot ? "WRITE-PROTECTED " : " ",
761 src_path);
762 if (!reply(from_parent, write_prot && !o_force))
763 return FALSE;
765 if (S_ISDIR(info.st_mode))
767 char *safe_path;
768 safe_path = g_strdup(src_path);
769 for_dir_contents(do_delete, safe_path, safe_path);
770 if (rmdir(safe_path))
772 g_free(safe_path);
773 send_error();
774 return FALSE;
776 g_string_assign(message, "'Directory deleted\n");
777 send();
778 g_free(safe_path);
780 else if (unlink(src_path) == 0)
782 g_string_sprintf(message, "'Deleted '%s'\n", src_path);
783 send();
785 else
787 send_error();
788 return FALSE;
791 return TRUE;
794 static gboolean do_copy(char *path, char *dest)
796 char *dest_path;
797 char *leaf;
798 struct stat info;
799 struct stat dest_info;
800 gboolean retval = TRUE;
802 check_flags();
804 leaf = strrchr(path, '/');
805 if (!leaf)
806 leaf = path; /* Error? */
807 else
808 leaf++;
810 dest_path = make_path(dest, leaf)->str;
812 if (lstat(path, &info))
814 send_error();
815 return FALSE;
818 if (lstat(dest_path, &dest_info) == 0)
820 int err;
821 gboolean merge;
823 merge = S_ISDIR(info.st_mode) && S_ISDIR(dest_info.st_mode);
825 g_string_sprintf(message, "?'%s' already exists - %s?",
826 dest_path,
827 merge ? "merge contents" : "overwrite");
829 if (!reply(from_parent, TRUE))
830 return FALSE;
832 if (!merge)
834 if (S_ISDIR(dest_info.st_mode))
835 err = rmdir(dest_path);
836 else
837 err = unlink(dest_path);
839 if (err)
841 send_error();
842 if (errno != ENOENT)
843 return FALSE;
844 g_string_sprintf(message,
845 "'Trying copy anyway...\n");
846 send();
850 else if (!quiet)
852 g_string_sprintf(message, "?Copy %s as %s?", path, dest_path);
853 if (!reply(from_parent, FALSE))
854 return FALSE;
856 else
858 g_string_sprintf(message, "'Copying %s as %s\n", path,
859 dest_path);
860 send();
863 if (S_ISDIR(info.st_mode))
865 char *safe_path, *safe_dest;
866 struct stat dest_info;
867 gboolean exists;
869 /* (we will do the update ourselves now, rather than
870 * afterwards)
872 retval = FALSE;
874 safe_path = g_strdup(path);
875 safe_dest = g_strdup(dest_path);
877 exists = !lstat(dest_path, &dest_info);
879 if (exists && !S_ISDIR(dest_info.st_mode))
881 g_string_sprintf(message,
882 "!ERROR: Destination already exists, "
883 "but is not a directory\n");
885 else if (exists == FALSE && mkdir(dest_path, info.st_mode))
886 send_error();
887 else
889 if (!exists)
891 /* (just been created then) */
892 g_string_sprintf(message, "+%s", dest);
893 send();
896 for_dir_contents(do_copy, safe_path, safe_dest);
897 /* Note: dest_path now invalid... */
900 g_free(safe_path);
901 g_free(safe_dest);
903 else if (S_ISLNK(info.st_mode))
905 char target[MAXPATHLEN + 1];
906 int count;
908 /* Not all versions of cp(1) can make symlinks,
909 * so we special-case it.
912 count = readlink(path, target, sizeof(target) - 1);
913 if (count < 0)
915 send_error();
916 retval = FALSE;
918 else
920 target[count] = '\0';
921 if (symlink(target, dest_path))
923 send_error();
924 retval = FALSE;
928 else
930 char *argv[] = {"cp", "-pRf", NULL, NULL, NULL};
932 argv[2] = path;
933 argv[3] = dest_path;
935 if (fork_exec_wait(argv))
937 g_string_sprintf(message, "!ERROR: %s\n",
938 "Copy failed\n");
939 send();
940 retval = FALSE;
944 return retval;
947 static gboolean do_move(char *path, char *dest)
949 char *dest_path;
950 char *leaf;
951 gboolean retval = TRUE;
952 char *argv[] = {"mv", "-f", NULL, NULL, NULL};
954 check_flags();
956 leaf = strrchr(path, '/');
957 if (!leaf)
958 leaf = path; /* Error? */
959 else
960 leaf++;
962 dest_path = make_path(dest, leaf)->str;
964 if (access(dest_path, F_OK) == 0)
966 struct stat info;
967 int err;
969 g_string_sprintf(message, "?'%s' already exists - overwrite?",
970 dest_path);
971 if (!reply(from_parent, TRUE))
972 return FALSE;
974 if (lstat(dest_path, &info))
976 send_error();
977 return FALSE;
980 if (S_ISDIR(info.st_mode))
981 err = rmdir(dest_path);
982 else
983 err = unlink(dest_path);
985 if (err)
987 send_error();
988 if (errno != ENOENT)
989 return FALSE;
990 g_string_sprintf(message,
991 "'Trying move anyway...\n");
992 send();
995 else if (!quiet)
997 g_string_sprintf(message, "?Move %s as %s?", path, dest_path);
998 if (!reply(from_parent, FALSE))
999 return FALSE;
1001 else
1003 g_string_sprintf(message, "'Moving %s as %s\n", path,
1004 dest_path);
1005 send();
1008 argv[2] = path;
1009 argv[3] = dest;
1011 if (fork_exec_wait(argv) == 0)
1013 g_string_sprintf(message, "+%s", path);
1014 g_string_truncate(message, leaf - path);
1015 send();
1017 else
1019 g_string_sprintf(message, "!ERROR: Failed to move %s as %s\n",
1020 path, dest_path);
1021 retval = FALSE;
1023 send();
1025 return retval;
1028 static gboolean do_link(char *path, char *dest)
1030 char *dest_path;
1031 char *leaf;
1033 check_flags();
1035 leaf = strrchr(path, '/');
1036 if (!leaf)
1037 leaf = path; /* Error? */
1038 else
1039 leaf++;
1041 dest_path = make_path(dest, leaf)->str;
1043 if (quiet)
1045 g_string_sprintf(message, "'Linking %s as %s\n", path,
1046 dest_path);
1047 send();
1049 else
1051 g_string_sprintf(message, "?Link %s as %s?", path, dest_path);
1052 if (!reply(from_parent, FALSE))
1053 return FALSE;
1056 if (symlink(path, dest_path))
1058 send_error();
1059 return FALSE;
1062 return TRUE;
1065 /* Mount/umount this item */
1066 static void do_mount(FilerWindow *filer_window, DirItem *item)
1068 char *argv[3] = {NULL, NULL, NULL};
1070 check_flags();
1072 argv[0] = item->flags & ITEM_FLAG_MOUNTED ? "umount" : "mount";
1073 argv[1] = make_path(filer_window->path, item->leafname)->str;
1075 if (quiet)
1077 g_string_sprintf(message, "'%sing %s\n", argv[0], argv[1]);
1078 send();
1080 else
1082 g_string_sprintf(message, "?%s %s?", argv[0], argv[1]);
1083 if (!reply(from_parent, FALSE))
1084 return;
1087 if (fork_exec_wait(argv) == 0)
1089 g_string_sprintf(message, "+%s", filer_window->path);
1090 send();
1091 g_string_sprintf(message, "m%s", argv[1]);
1092 send();
1094 else
1096 g_string_sprintf(message, "!ERROR: %s failed\n", argv[0]);
1097 send();
1101 /* CHILD MAIN LOOPS */
1103 /* After forking, the child calls one of these functions */
1105 static void usage_cb(gpointer data)
1107 FilerWindow *filer_window = (FilerWindow *) data;
1108 Collection *collection = filer_window->collection;
1109 DirItem *item;
1110 int left = collection->number_selected;
1111 int i = -1;
1112 off_t total_size = 0;
1114 send_dir(filer_window->path);
1116 while (left > 0)
1118 i++;
1119 if (!collection->items[i].selected)
1120 continue;
1121 item = (DirItem *) collection->items[i].data;
1122 size_tally = 0;
1123 do_usage(make_path(filer_window->path,
1124 item->leafname)->str,
1125 filer_window->path);
1126 g_string_sprintf(message, "'%s: %s\n",
1127 item->leafname,
1128 format_size((unsigned long) size_tally));
1129 send();
1130 total_size += size_tally;
1131 left--;
1134 g_string_sprintf(message, "'\nTotal: %s\n",
1135 format_size((unsigned long) total_size));
1136 send();
1139 #ifdef DO_MOUNT_POINTS
1140 static void mount_cb(gpointer data)
1142 FilerWindow *filer_window = (FilerWindow *) data;
1143 Collection *collection = filer_window->collection;
1144 DirItem *item;
1145 int i;
1146 gboolean mount_points = FALSE;
1148 send_dir(filer_window->path);
1150 if (mount_item)
1151 do_mount(filer_window, mount_item);
1152 else
1154 for (i = 0; i < collection->number_of_items; i++)
1156 if (!collection->items[i].selected)
1157 continue;
1158 item = (DirItem *) collection->items[i].data;
1159 if (!(item->flags & ITEM_FLAG_MOUNT_POINT))
1160 continue;
1161 mount_points = TRUE;
1163 do_mount(filer_window, item);
1166 if (!mount_points)
1168 g_string_sprintf(message,
1169 "!No mount points selected!\n");
1170 send();
1174 g_string_sprintf(message, "'\nDone\n");
1175 send();
1177 #endif
1179 static void delete_cb(gpointer data)
1181 FilerWindow *filer_window = (FilerWindow *) data;
1182 Collection *collection = filer_window->collection;
1183 DirItem *item;
1184 int left = collection->number_selected;
1185 int i = -1;
1187 send_dir(filer_window->path);
1189 while (left > 0)
1191 i++;
1192 if (!collection->items[i].selected)
1193 continue;
1194 item = (DirItem *) collection->items[i].data;
1195 if (do_delete(make_path(filer_window->path,
1196 item->leafname)->str,
1197 filer_window->path))
1199 g_string_sprintf(message, "+%s", filer_window->path);
1200 send();
1202 left--;
1205 g_string_sprintf(message, "'\nDone\n");
1206 send();
1209 static void list_cb(gpointer data)
1211 GSList *paths = (GSList *) data;
1213 while (paths)
1215 send_dir((char *) paths->data);
1217 if (action_do_func((char *) paths->data, action_dest))
1219 g_string_sprintf(message, "+%s", action_dest);
1220 send();
1223 paths = paths->next;
1226 g_string_sprintf(message, "'\nDone\n");
1227 send();
1230 static void add_toggle(GUIside *gui_side, guchar *label, guchar *code)
1232 GtkWidget *check;
1234 check = gtk_check_button_new_with_label(label);
1235 gtk_object_set_data(GTK_OBJECT(check), "send-code", code);
1236 gtk_signal_connect(GTK_OBJECT(check), "clicked",
1237 button_reply, gui_side);
1238 gtk_box_pack_start(GTK_BOX(gui_side->vbox), check, FALSE, TRUE, 0);
1242 /* EXTERNAL INTERFACE */
1244 /* Count disk space used by selected items */
1245 void action_usage(FilerWindow *filer_window)
1247 GUIside *gui_side;
1248 Collection *collection;
1250 collection = filer_window->collection;
1252 if (collection->number_selected < 1)
1254 report_error("ROX-Filer", "You need to select some items "
1255 "to count");
1256 return;
1259 gui_side = start_action(filer_window, usage_cb, TRUE);
1260 if (!gui_side)
1261 return;
1263 gui_side->show_info = TRUE;
1265 gtk_window_set_title(GTK_WINDOW(gui_side->window), "Disk Usage");
1266 number_of_windows++;
1267 gtk_widget_show_all(gui_side->window);
1270 /* Mount/unmount 'item', or all selected mount points if NULL. */
1271 void action_mount(FilerWindow *filer_window, DirItem *item)
1273 #ifdef DO_MOUNT_POINTS
1274 GUIside *gui_side;
1275 Collection *collection;
1277 collection = filer_window->collection;
1279 if (item == NULL && collection->number_selected < 1)
1281 report_error("ROX-Filer", "You need to select some items "
1282 "to mount or unmount");
1283 return;
1286 mount_item = item;
1287 gui_side = start_action(filer_window, mount_cb, o_auto_mount);
1288 if (!gui_side)
1289 return;
1291 gtk_window_set_title(GTK_WINDOW(gui_side->window), "Mount / Unmount");
1292 number_of_windows++;
1293 gtk_widget_show_all(gui_side->window);
1294 #else
1295 report_error("ROX-Filer",
1296 "ROX-Filer does not yet support mount points on your "
1297 "system. Sorry.");
1298 #endif /* DO_MOUNT_POINTS */
1301 /* Deletes all selected items in the window */
1302 void action_delete(FilerWindow *filer_window)
1304 GUIside *gui_side;
1305 Collection *collection;
1307 collection = filer_window->collection;
1309 if (collection->number_selected < 1)
1311 report_error("ROX-Filer", "You need to select some items "
1312 "to delete");
1313 return;
1316 gui_side = start_action(filer_window, delete_cb, o_auto_delete);
1317 if (!gui_side)
1318 return;
1320 gtk_window_set_title(GTK_WINDOW(gui_side->window), "Delete");
1321 add_toggle(gui_side,
1322 "Don't confirm deletion of non-writeable items", "F");
1324 number_of_windows++;
1325 gtk_widget_show_all(gui_side->window);
1328 void action_copy(GSList *paths, char *dest)
1330 GUIside *gui_side;
1332 action_dest = dest;
1333 action_do_func = do_copy;
1334 gui_side = start_action(paths, list_cb, o_auto_copy);
1335 if (!gui_side)
1336 return;
1338 gtk_window_set_title(GTK_WINDOW(gui_side->window), "Copy");
1339 number_of_windows++;
1340 gtk_widget_show_all(gui_side->window);
1343 void action_move(GSList *paths, char *dest)
1345 GUIside *gui_side;
1347 action_dest = dest;
1348 action_do_func = do_move;
1349 gui_side = start_action(paths, list_cb, o_auto_move);
1350 if (!gui_side)
1351 return;
1353 gtk_window_set_title(GTK_WINDOW(gui_side->window), "Move");
1354 number_of_windows++;
1355 gtk_widget_show_all(gui_side->window);
1358 void action_link(GSList *paths, char *dest)
1360 GUIside *gui_side;
1362 action_dest = dest;
1363 action_do_func = do_link;
1364 gui_side = start_action(paths, list_cb, o_auto_link);
1365 if (!gui_side)
1366 return;
1368 gtk_window_set_title(GTK_WINDOW(gui_side->window), "Link");
1369 number_of_windows++;
1370 gtk_widget_show_all(gui_side->window);
1373 void action_init(void)
1375 options_sections = g_slist_prepend(options_sections, &options);
1376 option_register("action_auto_quiet", action_auto_quiet);