r217: Merged the MIME-Type and Special fields in the info window into a single
[rox-filer.git] / ROX-Filer / src / action.c
blob6132ec6b089f5b78f017f135eb2f10de752ad228
1 /*
2 * $Id$
4 * ROX-Filer, filer for the ROX desktop project
5 * Copyright (C) 2000, 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"
47 #include "modechange.h"
49 /* Options bits */
50 static GtkWidget *create_options();
51 static void update_options();
52 static void set_options();
53 static void save_options();
54 static char *action_auto_quiet(char *data);
56 static OptionsSection options =
58 "Action window options",
59 create_options,
60 update_options,
61 set_options,
62 save_options
65 static gboolean o_auto_copy = TRUE;
66 static gboolean o_auto_move = TRUE;
67 static gboolean o_auto_link = TRUE;
68 static gboolean o_auto_delete = FALSE;
69 static gboolean o_auto_mount = TRUE;
71 static GtkWidget *w_auto_copy = NULL;
72 static GtkWidget *w_auto_move = NULL;
73 static GtkWidget *w_auto_link = NULL;
74 static GtkWidget *w_auto_delete = NULL;
75 static GtkWidget *w_auto_mount = NULL;
77 static GdkColor red = {0, 0xffff, 0, 0};
79 /* Parent->Child messages are one character each:
81 * Q/Y/N Quiet/Yes/No button clicked
82 * F Force deletion of non-writeable items
85 #define SENSITIVE_YESNO(gui_side, state) \
86 do { \
87 gtk_widget_set_sensitive((gui_side)->yes, state); \
88 gtk_widget_set_sensitive((gui_side)->no, state); \
89 if ((gui_side)->entry) \
90 gtk_widget_set_sensitive((gui_side)->entry, state);\
91 } while (0)
93 #define ON(flag) ((flag) ? "on" : "off")
95 typedef struct _GUIside GUIside;
96 typedef void ActionChild(gpointer data);
97 typedef gboolean ForDirCB(char *path, char *dest_path);
99 struct _GUIside
101 int from_child; /* File descriptor */
102 FILE *to_child;
103 int input_tag; /* gdk_input_add() */
104 GtkWidget *vbox, *log, *window, *dir;
105 GtkWidget *quiet, *yes, *no;
106 GtkWidget *entry; /* May be NULL */
107 int child; /* Process ID */
108 int errors;
109 gboolean show_info; /* For Disk Usage */
111 char *next_dir; /* NULL => no timer active */
112 gint next_timer;
115 /* These don't need to be in a structure because we fork() before
116 * using them again.
118 static int from_parent = 0;
119 static FILE *to_parent = NULL;
120 static gboolean quiet = FALSE;
121 static GString *message = NULL;
122 static char *action_dest = NULL;
123 static char *action_leaf = NULL;
124 static gboolean (*action_do_func)(char *source, char *dest);
125 static size_t size_tally; /* For Disk Usage */
126 static DirItem *mount_item;
127 static struct mode_change *mode_change = NULL; /* For Permissions */
129 static gboolean o_force = FALSE;
130 static gboolean o_brief = FALSE;
131 static gboolean o_recurse = FALSE;
133 /* This is the last chmod command set, so that bringing up a new
134 * chmod box defaults to the last used command.
136 static guchar *chmod_command = NULL;
138 /* Static prototypes */
139 static gboolean send();
140 static gboolean send_error();
141 static gboolean send_dir(char *dir);
142 static gboolean read_exact(int source, char *buffer, ssize_t len);
143 static void do_mount(FilerWindow *filer_window, DirItem *item);
144 static void add_toggle(GUIside *gui_side, guchar *label, guchar *code);
145 static gboolean reply(int fd, gboolean ignore_quiet);
147 /* OPTIONS */
149 /* Build up some option widgets to go in the options dialog, but don't
150 * fill them in yet.
152 static GtkWidget *create_options()
154 GtkWidget *vbox, *hbox, *label;
156 vbox = gtk_vbox_new(FALSE, 0);
157 gtk_container_set_border_width(GTK_CONTAINER(vbox), 4);
159 label = gtk_label_new("Auto-start (Quiet) these actions:");
160 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, TRUE, 0);
162 hbox = gtk_hbox_new(TRUE, 0);
163 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0);
165 w_auto_copy = gtk_check_button_new_with_label("Copy");
166 gtk_box_pack_start(GTK_BOX(hbox), w_auto_copy, FALSE, TRUE, 0);
167 w_auto_move = gtk_check_button_new_with_label("Move");
168 gtk_box_pack_start(GTK_BOX(hbox), w_auto_move, FALSE, TRUE, 0);
169 w_auto_link = gtk_check_button_new_with_label("Link");
170 gtk_box_pack_start(GTK_BOX(hbox), w_auto_link, FALSE, TRUE, 0);
171 w_auto_delete = gtk_check_button_new_with_label("Delete");
172 gtk_box_pack_start(GTK_BOX(hbox), w_auto_delete, FALSE, TRUE, 0);
173 w_auto_mount = gtk_check_button_new_with_label("Mount");
174 gtk_box_pack_start(GTK_BOX(hbox), w_auto_mount, FALSE, TRUE, 0);
176 return vbox;
179 /* Reflect current state by changing the widgets in the options box */
180 static void update_options()
182 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w_auto_copy),
183 o_auto_copy);
184 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w_auto_move),
185 o_auto_move);
186 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w_auto_link),
187 o_auto_link);
188 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w_auto_delete),
189 o_auto_delete);
190 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w_auto_mount),
191 o_auto_mount);
194 /* Set current values by reading the states of the widgets in the options box */
195 static void set_options()
197 o_auto_copy = gtk_toggle_button_get_active(
198 GTK_TOGGLE_BUTTON(w_auto_copy));
199 o_auto_move = gtk_toggle_button_get_active(
200 GTK_TOGGLE_BUTTON(w_auto_move));
201 o_auto_link = gtk_toggle_button_get_active(
202 GTK_TOGGLE_BUTTON(w_auto_link));
203 o_auto_delete = gtk_toggle_button_get_active(
204 GTK_TOGGLE_BUTTON(w_auto_delete));
205 o_auto_mount = gtk_toggle_button_get_active(
206 GTK_TOGGLE_BUTTON(w_auto_mount));
209 static void save_options()
211 guchar str[] = "cmldt";
213 if (o_auto_copy)
214 str[0] = 'C';
215 if (o_auto_move)
216 str[1] = 'M';
217 if (o_auto_link)
218 str[2] = 'L';
219 if (o_auto_delete)
220 str[3] = 'D';
221 if (o_auto_mount)
222 str[4] = 'T';
224 option_write("action_auto_quiet", str);
227 static char *action_auto_quiet(char *data)
229 while (*data)
231 char c = *data++;
232 gboolean state;
234 state = isupper(c);
236 switch (tolower(c))
238 case 'c':
239 o_auto_copy = state;
240 break;
241 case 'm':
242 o_auto_move = state;
243 break;
244 case 'l':
245 o_auto_link = state;
246 break;
247 case 'd':
248 o_auto_delete = state;
249 break;
250 case 't':
251 o_auto_mount = state;
252 break;
253 default:
254 return "Unknown flag";
258 return NULL;
261 /* SUPPORT */
263 /* This is called whenever the user edits the command - send the new command
264 * to the child process.
266 static void chmod_command_changed(GtkEntry *entry, GUIside *gui_side)
268 g_free(chmod_command);
269 chmod_command = g_strdup(gtk_entry_get_text(entry));
271 if (!gui_side->to_child)
272 return;
274 fputc('P', gui_side->to_child);
275 fputs(chmod_command, gui_side->to_child);
276 fputc('\n', gui_side->to_child);
277 fflush(gui_side->to_child);
280 static void show_chmod_help(gpointer data)
282 static GtkWidget *help = NULL;
284 if (!help)
286 GtkWidget *text, *vbox, *button, *hbox, *sep;
288 help = gtk_window_new(GTK_WINDOW_DIALOG);
289 gtk_container_set_border_width(GTK_CONTAINER(help), 10);
290 gtk_window_set_title(GTK_WINDOW(help),
291 "Permissions command reference");
293 vbox = gtk_vbox_new(FALSE, 0);
294 gtk_container_add(GTK_CONTAINER(help), vbox);
296 text = gtk_label_new(
297 "The format of a command is:\n"
298 "CHANGE, CHANGE, ...\n"
299 "Each CHANGE is:\n"
300 "WHO HOW PERMISSIONS\n"
301 "WHO is some combination of u, g and o which determines whether to\n"
302 "change the permissions for the User (owner), Group or Others.\n"
303 "HOW is +, - or = to add, remove or set exactly the permissions.\n"
304 "PERMISSIONS is some combination of the letters 'rwxXstugo'\n\n"
306 "Examples:\n"
307 "u+rw (the file owner gains read and write permission)\n"
308 "g=u (the group permissions are set to be the same as the user's)\n"
309 "o=u-w (others get the same permissions as the owner, but without "
310 "write permission)\n"
311 "a+x (everyone gets execute/access permission - same as 'ugo+x')\n"
312 "a+X (directories become accessable by everyone; files which were\n"
313 "executable by anyone become executable by everyone)\n"
314 "u+rw, go+r (two commands at once!)\n"
315 "u+s (set the SetUID bit - often has no effect on script files)\n"
316 "755 (set the permissions directly)\n"
318 "\nSee the chmod(1) man page for full details.");
319 gtk_label_set_justify(GTK_LABEL(text), GTK_JUSTIFY_LEFT);
320 gtk_box_pack_start(GTK_BOX(vbox), text, TRUE, TRUE, 0);
322 hbox = gtk_hbox_new(FALSE, 20);
323 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0);
325 sep = gtk_hseparator_new();
326 gtk_box_pack_start(GTK_BOX(hbox), sep, TRUE, TRUE, 0);
327 button = gtk_button_new_with_label("Close");
328 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, TRUE, 0);
329 gtk_signal_connect_object(GTK_OBJECT(button), "clicked",
330 gtk_widget_hide, GTK_OBJECT(help));
332 gtk_signal_connect_object(GTK_OBJECT(help), "delete_event",
333 gtk_widget_hide, GTK_OBJECT(help));
336 if (GTK_WIDGET_VISIBLE(help))
337 gtk_widget_hide(help);
338 gtk_widget_show_all(help);
341 /* TRUE iff `sub' is (or would be) an object inside the directory `parent',
342 * (or the two are the same directory)
344 static gboolean is_sub_dir(char *sub, char *parent)
346 int parent_len;
347 guchar *real_sub, *real_parent;
348 gboolean retval;
350 real_sub = pathdup(sub);
351 real_parent = pathdup(parent);
353 parent_len = strlen(real_parent);
354 if (strncmp(real_parent, real_sub, parent_len))
355 retval = FALSE;
356 else
358 /* real_sub is at least as long as real_parent and all
359 * characters upto real_parent's length match.
362 retval = real_sub[parent_len] == '\0' ||
363 real_sub[parent_len] == '/';
366 g_free(real_sub);
367 g_free(real_parent);
369 return retval;
372 static gboolean display_dir(gpointer data)
374 GUIside *gui_side = (GUIside *) data;
376 gtk_label_set_text(GTK_LABEL(gui_side->dir), gui_side->next_dir + 1);
377 g_free(gui_side->next_dir);
378 gui_side->next_dir = NULL;
380 return FALSE;
383 /* Called when the child sends us a message */
384 static void message_from_child(gpointer data,
385 gint source,
386 GdkInputCondition condition)
388 char buf[5];
389 GUIside *gui_side = (GUIside *) data;
390 GtkWidget *log = gui_side->log;
392 if (read_exact(source, buf, 4))
394 ssize_t message_len;
395 char *buffer;
397 buf[4] = '\0';
398 message_len = strtol(buf, NULL, 16);
399 buffer = g_malloc(message_len + 1);
400 if (message_len > 0 && read_exact(source, buffer, message_len))
402 buffer[message_len] = '\0';
403 if (*buffer == '?')
404 SENSITIVE_YESNO(gui_side, TRUE);
405 else if (*buffer == '+')
407 refresh_dirs(buffer + 1);
408 g_free(buffer);
409 return;
411 else if (*buffer == 'm')
413 filer_check_mounted(buffer + 1);
414 g_free(buffer);
415 return;
417 else if (*buffer == '/')
419 if (gui_side->next_dir)
420 g_free(gui_side->next_dir);
421 else
422 gui_side->next_timer =
423 gtk_timeout_add(500,
424 display_dir,
425 gui_side);
426 gui_side->next_dir = buffer;
427 return;
429 else if (*buffer == '!')
430 gui_side->errors++;
432 gtk_text_insert(GTK_TEXT(log),
433 NULL,
434 *buffer == '!' ? &red : NULL,
435 NULL,
436 buffer + 1, message_len - 1);
437 g_free(buffer);
438 return;
440 g_print("Child died in the middle of a message.\n");
443 /* The child is dead */
444 gui_side->child = 0;
446 fclose(gui_side->to_child);
447 gui_side->to_child = NULL;
448 close(gui_side->from_child);
449 gdk_input_remove(gui_side->input_tag);
450 gtk_widget_set_sensitive(gui_side->quiet, FALSE);
452 if (gui_side->errors)
454 GString *report;
455 report = g_string_new(NULL);
456 g_string_sprintf(report, "There %s %d error%s.\n",
457 gui_side->errors == 1 ? "was" : "were",
458 gui_side->errors,
459 gui_side->errors == 1 ? "" : "s");
460 gtk_text_insert(GTK_TEXT(log), NULL, &red, NULL,
461 report->str, report->len);
463 g_string_free(report, TRUE);
465 else if (gui_side->show_info == FALSE)
466 gtk_widget_destroy(gui_side->window);
469 /* Scans src_dir, updating dest_path whenever cb returns TRUE */
470 static void for_dir_contents(ForDirCB *cb, char *src_dir, char *dest_path)
472 DIR *d;
473 struct dirent *ent;
474 GSList *list = NULL, *next;
476 d = opendir(src_dir);
477 if (!d)
479 send_error();
480 return;
483 send_dir(src_dir);
485 while ((ent = readdir(d)))
487 if (ent->d_name[0] == '.' && (ent->d_name[1] == '\0'
488 || (ent->d_name[1] == '.' && ent->d_name[2] == '\0')))
489 continue;
490 list = g_slist_append(list, g_strdup(make_path(src_dir,
491 ent->d_name)->str));
493 closedir(d);
495 if (!list)
496 return;
498 next = list;
500 while (next)
502 if (cb((char *) next->data, dest_path))
504 g_string_sprintf(message, "+%s", dest_path);
505 send();
508 g_free(next->data);
509 next = next->next;
511 g_slist_free(list);
512 return;
515 /* Read this many bytes into the buffer. TRUE on success. */
516 static gboolean read_exact(int source, char *buffer, ssize_t len)
518 while (len > 0)
520 ssize_t got;
521 got = read(source, buffer, len);
522 if (got < 1)
523 return FALSE;
524 len -= got;
525 buffer += got;
527 return TRUE;
530 /* Send 'message' to our parent process. TRUE on success. */
531 static gboolean send()
533 char len_buffer[5];
534 ssize_t len;
536 g_return_val_if_fail(message->len < 0xffff, FALSE);
538 sprintf(len_buffer, "%04x", message->len);
539 fwrite(len_buffer, 1, 4, to_parent);
540 len = fwrite(message->str, 1, message->len, to_parent);
541 fflush(to_parent);
542 return len == message->len;
545 /* Set the directory indicator at the top of the window */
546 static gboolean send_dir(char *dir)
548 g_string_sprintf(message, "/%s", dir);
549 return send();
552 static gboolean send_error()
554 g_string_sprintf(message, "!ERROR: %s\n", g_strerror(errno));
555 return send();
558 static void button_reply(GtkWidget *button, GUIside *gui_side)
560 char *text;
562 if (!gui_side->to_child)
563 return;
565 text = gtk_object_get_data(GTK_OBJECT(button), "send-code");
566 g_return_if_fail(text != NULL);
567 fputc(*text, gui_side->to_child);
568 fflush(gui_side->to_child);
570 if (*text == 'Y' || *text == 'N' || *text == 'Q')
571 SENSITIVE_YESNO(gui_side, FALSE);
574 static void process_flag(char flag)
576 switch (flag)
578 case 'Q':
579 quiet = !quiet;
580 break;
581 case 'F':
582 o_force = !o_force;
583 break;
584 case 'R':
585 o_recurse = !o_recurse;
586 break;
587 case 'B':
588 o_brief = !o_brief;
589 break;
590 default:
591 g_string_sprintf(message,
592 "!ERROR: Bad message '%c'\n", flag);
593 send();
594 break;
598 /* If the parent has sent any flag toggles, read them */
599 static void check_flags(void)
601 fd_set set;
602 int got;
603 char retval;
604 struct timeval tv;
606 FD_ZERO(&set);
608 while (1)
610 FD_SET(from_parent, &set);
611 tv.tv_sec = 0;
612 tv.tv_usec = 0;
613 got = select(from_parent + 1, &set, NULL, NULL, &tv);
615 if (got == -1)
616 g_error("select() failed: %s\n", g_strerror(errno));
617 else if (!got)
618 return;
620 got = read(from_parent, &retval, 1);
621 if (got != 1)
622 g_error("read() error: %s\n", g_strerror(errno));
624 process_flag(retval);
628 static void read_new_chmod_command(void)
630 int len;
631 char c;
632 GString *new;
634 new = g_string_new(NULL);
636 for (;;)
638 len = read(from_parent, &c, 1);
639 if (len != 1)
641 fprintf(stderr, "read() error: %s\n",
642 g_strerror(errno));
643 _exit(1); /* Parent died? */
646 if (c == '\n')
647 break;
648 g_string_append_c(new, c);
651 if (mode_change)
652 mode_free(mode_change);
653 mode_change = mode_compile(new->str, MODE_MASK_ALL);
654 g_string_free(new, TRUE);
657 /* Read until the user sends a reply. If ignore_quiet is TRUE then
658 * the user MUST click Yes or No, else treat quiet on as Yes.
659 * If the user needs prompting then does send().
661 static gboolean reply(int fd, gboolean ignore_quiet)
663 ssize_t len;
664 char retval;
665 gboolean asked = FALSE;
667 while (ignore_quiet || !quiet)
669 if (!asked)
671 send();
672 asked = TRUE;
675 len = read(fd, &retval, 1);
676 if (len != 1)
678 fprintf(stderr, "read() error: %s\n",
679 g_strerror(errno));
680 _exit(1); /* Parent died? */
683 switch (retval)
685 case 'Q':
686 quiet = !quiet;
687 if (ignore_quiet)
689 g_string_assign(message, "?");
690 send();
692 break;
693 case 'Y':
694 g_string_assign(message, "' Yes\n");
695 send();
696 return TRUE;
697 case 'N':
698 g_string_assign(message, "' No\n");
699 send();
700 return FALSE;
701 case 'P':
702 read_new_chmod_command();
703 break;
704 default:
705 process_flag(retval);
706 break;
710 if (asked)
712 g_string_assign(message, "' Quiet\n");
713 send();
715 return TRUE;
718 static void destroy_action_window(GtkWidget *widget, gpointer data)
720 GUIside *gui_side = (GUIside *) data;
722 if (gui_side->child)
724 kill(gui_side->child, SIGTERM);
725 fclose(gui_side->to_child);
726 close(gui_side->from_child);
727 gdk_input_remove(gui_side->input_tag);
730 if (gui_side->next_dir)
732 gtk_timeout_remove(gui_side->next_timer);
733 g_free(gui_side->next_dir);
735 g_free(gui_side);
737 if (--number_of_windows < 1)
738 gtk_main_quit();
741 /* Create two pipes, fork() a child and return a pointer to a GUIside struct
742 * (NULL on failure). The child calls func().
744 * If autoq then automatically selects 'Quiet'.
746 static GUIside *start_action(gpointer data, ActionChild *func, gboolean autoq)
748 int filedes[4]; /* 0 and 2 are for reading */
749 GUIside *gui_side;
750 int child;
751 GtkWidget *vbox, *button, *hbox, *scrollbar, *actions;
752 struct sigaction act;
754 if (pipe(filedes))
756 report_error("ROX-Filer", g_strerror(errno));
757 return NULL;
760 if (pipe(filedes + 2))
762 close(filedes[0]);
763 close(filedes[1]);
764 report_error("ROX-Filer", g_strerror(errno));
765 return NULL;
768 child = fork();
769 switch (child)
771 case -1:
772 report_error("ROX-Filer", g_strerror(errno));
773 return NULL;
774 case 0:
775 /* We are the child */
777 quiet = autoq;
779 /* Reset the SIGCHLD handler */
780 act.sa_handler = SIG_DFL;
781 sigemptyset(&act.sa_mask);
782 act.sa_flags = 0;
783 sigaction(SIGCHLD, &act, NULL);
785 message = g_string_new(NULL);
786 close(filedes[0]);
787 close(filedes[3]);
788 to_parent = fdopen(filedes[1], "wb");
789 from_parent = filedes[2];
790 func(data);
791 _exit(0);
794 /* We are the parent */
795 close(filedes[1]);
796 close(filedes[2]);
797 gui_side = g_malloc(sizeof(GUIside));
798 gui_side->from_child = filedes[0];
799 gui_side->to_child = fdopen(filedes[3], "wb");
800 gui_side->log = NULL;
801 gui_side->child = child;
802 gui_side->errors = 0;
803 gui_side->show_info = FALSE;
804 gui_side->entry = NULL;
806 gui_side->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
807 gtk_container_set_border_width(GTK_CONTAINER(gui_side->window), 2);
808 gtk_window_set_default_size(GTK_WINDOW(gui_side->window), 450, 200);
809 gtk_signal_connect(GTK_OBJECT(gui_side->window), "destroy",
810 GTK_SIGNAL_FUNC(destroy_action_window), gui_side);
812 gui_side->vbox = vbox = gtk_vbox_new(FALSE, 0);
813 gtk_container_add(GTK_CONTAINER(gui_side->window), vbox);
815 gui_side->dir = gtk_label_new("<dir>");
816 gui_side->next_dir = NULL;
817 gtk_misc_set_alignment(GTK_MISC(gui_side->dir), 0.5, 0.5);
818 gtk_box_pack_start(GTK_BOX(vbox), gui_side->dir, FALSE, TRUE, 0);
820 hbox = gtk_hbox_new(FALSE, 0);
821 gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 4);
823 gui_side->log = gtk_text_new(NULL, NULL);
824 gtk_widget_set_usize(gui_side->log, 400, 100);
825 gtk_box_pack_start(GTK_BOX(hbox), gui_side->log, TRUE, TRUE, 0);
826 scrollbar = gtk_vscrollbar_new(GTK_TEXT(gui_side->log)->vadj);
827 gtk_box_pack_start(GTK_BOX(hbox), scrollbar, FALSE, TRUE, 0);
829 actions = gtk_hbox_new(TRUE, 4);
830 gtk_box_pack_start(GTK_BOX(vbox), actions, FALSE, TRUE, 0);
832 gui_side->quiet = button = gtk_toggle_button_new_with_label("Quiet");
833 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), autoq);
834 gtk_object_set_data(GTK_OBJECT(button), "send-code", "Q");
835 gtk_box_pack_start(GTK_BOX(actions), button, TRUE, TRUE, 0);
836 gtk_signal_connect(GTK_OBJECT(button), "clicked",
837 button_reply, gui_side);
838 gui_side->yes = button = gtk_button_new_with_label("Yes");
839 gtk_object_set_data(GTK_OBJECT(button), "send-code", "Y");
840 gtk_box_pack_start(GTK_BOX(actions), button, TRUE, TRUE, 0);
841 gtk_signal_connect(GTK_OBJECT(button), "clicked",
842 button_reply, gui_side);
843 gui_side->no = button = gtk_button_new_with_label("No");
844 gtk_object_set_data(GTK_OBJECT(button), "send-code", "N");
845 gtk_box_pack_start(GTK_BOX(actions), button, TRUE, TRUE, 0);
846 gtk_signal_connect(GTK_OBJECT(button), "clicked",
847 button_reply, gui_side);
848 SENSITIVE_YESNO(gui_side, FALSE);
850 button = gtk_button_new_with_label("Abort");
851 gtk_box_pack_start(GTK_BOX(actions), button, TRUE, TRUE, 0);
852 gtk_signal_connect_object(GTK_OBJECT(button), "clicked",
853 gtk_widget_destroy, GTK_OBJECT(gui_side->window));
855 gui_side->input_tag = gdk_input_add(gui_side->from_child,
856 GDK_INPUT_READ,
857 message_from_child,
858 gui_side);
860 return gui_side;
863 /* ACTIONS ON ONE ITEM */
865 /* These may call themselves recursively, or ask questions, etc.
866 * TRUE iff the directory containing dest_path needs to be rescanned.
869 /* dest_path is the dir containing src_path.
870 * Updates the global size_tally.
872 static gboolean do_usage(char *src_path, char *dest_path)
874 struct stat info;
876 check_flags();
878 if (lstat(src_path, &info))
880 g_string_sprintf(message, "'%s:\n", src_path);
881 send();
882 send_error();
883 return FALSE;
886 if (S_ISREG(info.st_mode) || S_ISLNK(info.st_mode))
887 size_tally += info.st_size;
888 else if (S_ISDIR(info.st_mode))
890 g_string_sprintf(message, "?Count contents of %s?",
891 src_path);
892 if (reply(from_parent, FALSE))
894 char *safe_path;
895 safe_path = g_strdup(src_path);
896 for_dir_contents(do_usage, safe_path, safe_path);
897 g_free(safe_path);
901 return FALSE;
904 /* dest_path is the dir containing src_path */
905 static gboolean do_delete(char *src_path, char *dest_path)
907 struct stat info;
908 gboolean write_prot;
910 check_flags();
912 if (lstat(src_path, &info))
914 send_error();
915 return FALSE;
918 write_prot = S_ISLNK(info.st_mode) ? FALSE
919 : access(src_path, W_OK) != 0;
920 if (write_prot || !quiet)
922 g_string_sprintf(message, "?Delete %s'%s'?",
923 write_prot ? "WRITE-PROTECTED " : " ",
924 src_path);
925 if (!reply(from_parent, write_prot && !o_force))
926 return FALSE;
928 else if (!o_brief)
930 g_string_sprintf(message, "'Deleting '%s'\n", src_path);
931 send();
934 if (S_ISDIR(info.st_mode))
936 char *safe_path;
937 safe_path = g_strdup(src_path);
938 for_dir_contents(do_delete, safe_path, safe_path);
939 if (rmdir(safe_path))
941 g_free(safe_path);
942 send_error();
943 return FALSE;
945 g_string_sprintf(message, "'Directory '%s' deleted\n",
946 safe_path);
947 send();
948 g_string_sprintf(message, "m%s", safe_path);
949 send();
950 g_free(safe_path);
952 else if (unlink(src_path))
954 send_error();
955 return FALSE;
958 return TRUE;
961 /* path is the item to change. If is is a directory then we may recurse
962 * (depending on the recurse flag).
964 static gboolean do_chmod(char *path, char *dummy)
966 struct stat info;
967 mode_t new_mode;
969 check_flags();
971 if (lstat(path, &info))
973 send_error();
974 return FALSE;
976 if (S_ISLNK(info.st_mode))
977 return FALSE;
979 if (!quiet)
981 g_string_sprintf(message, "?Change permissions of '%s'?", path);
982 if (!reply(from_parent, FALSE))
983 return FALSE;
985 else if (!o_brief)
987 g_string_sprintf(message, "'Changing permissions of '%s'\n",
988 path);
989 send();
992 while (!mode_change)
994 g_string_assign(message,
995 "!Invalid mode command - change it and try again\n");
996 send();
997 g_string_sprintf(message, "?Change permissions of '%s'?", path);
998 if (!reply(from_parent, TRUE))
999 return FALSE;
1002 if (lstat(path, &info))
1004 send_error();
1005 return FALSE;
1007 if (S_ISLNK(info.st_mode))
1008 return FALSE;
1010 new_mode = mode_adjust(info.st_mode, mode_change);
1011 if (chmod(path, new_mode))
1013 send_error();
1014 return FALSE;
1017 if (o_recurse && S_ISDIR(info.st_mode))
1019 char *safe_path;
1020 safe_path = g_strdup(path);
1021 for_dir_contents(do_chmod, safe_path, safe_path);
1022 g_free(safe_path);
1025 return TRUE;
1028 /* We want to copy 'object' into directory 'dir'. If 'action_leaf'
1029 * is set then that is the new leafname, otherwise the leafname stays
1030 * the same.
1032 static char *make_dest_path(char *object, char *dir)
1034 char *leaf;
1036 if (action_leaf)
1037 leaf = action_leaf;
1038 else
1040 leaf = strrchr(object, '/');
1041 if (!leaf)
1042 leaf = object; /* Error? */
1043 else
1044 leaf++;
1047 return make_path(dir, leaf)->str;
1050 /* If action_leaf is not NULL it specifies the new leaf name
1052 static gboolean do_copy2(char *path, char *dest)
1054 char *dest_path;
1055 struct stat info;
1056 struct stat dest_info;
1057 gboolean retval = TRUE;
1059 check_flags();
1061 dest_path = make_dest_path(path, dest);
1063 if (lstat(path, &info))
1065 send_error();
1066 return FALSE;
1069 if (lstat(dest_path, &dest_info) == 0)
1071 int err;
1072 gboolean merge;
1074 merge = S_ISDIR(info.st_mode) && S_ISDIR(dest_info.st_mode);
1076 g_string_sprintf(message, "?'%s' already exists - %s?",
1077 dest_path,
1078 merge ? "merge contents" : "overwrite");
1080 if (!reply(from_parent, TRUE))
1081 return FALSE;
1083 if (!merge)
1085 if (S_ISDIR(dest_info.st_mode))
1086 err = rmdir(dest_path);
1087 else
1088 err = unlink(dest_path);
1090 if (err)
1092 send_error();
1093 if (errno != ENOENT)
1094 return FALSE;
1095 g_string_sprintf(message,
1096 "'Trying copy anyway...\n");
1097 send();
1101 else if (!quiet)
1103 g_string_sprintf(message, "?Copy %s as %s?", path, dest_path);
1104 if (!reply(from_parent, FALSE))
1105 return FALSE;
1107 else
1109 g_string_sprintf(message, "'Copying %s as %s\n", path,
1110 dest_path);
1111 send();
1114 if (S_ISDIR(info.st_mode))
1116 char *safe_path, *safe_dest;
1117 struct stat dest_info;
1118 gboolean exists;
1120 /* (we will do the update ourselves now, rather than
1121 * afterwards)
1123 retval = FALSE;
1125 safe_path = g_strdup(path);
1126 safe_dest = g_strdup(dest_path);
1128 exists = !lstat(dest_path, &dest_info);
1130 if (exists && !S_ISDIR(dest_info.st_mode))
1132 g_string_sprintf(message,
1133 "!ERROR: Destination already exists, "
1134 "but is not a directory\n");
1136 else if (exists == FALSE && mkdir(dest_path, info.st_mode))
1137 send_error();
1138 else
1140 if (!exists)
1142 /* (just been created then) */
1143 g_string_sprintf(message, "+%s", dest);
1144 send();
1147 action_leaf = NULL;
1148 for_dir_contents(do_copy2, safe_path, safe_dest);
1149 /* Note: dest_path now invalid... */
1152 g_free(safe_path);
1153 g_free(safe_dest);
1155 else if (S_ISLNK(info.st_mode))
1157 char target[MAXPATHLEN + 1];
1158 int count;
1160 /* Not all versions of cp(1) can make symlinks,
1161 * so we special-case it.
1164 count = readlink(path, target, sizeof(target) - 1);
1165 if (count < 0)
1167 send_error();
1168 retval = FALSE;
1170 else
1172 target[count] = '\0';
1173 if (symlink(target, dest_path))
1175 send_error();
1176 retval = FALSE;
1180 else
1182 char *argv[] = {"cp", "-pRf", NULL, NULL, NULL};
1184 argv[2] = path;
1185 argv[3] = dest_path;
1187 if (fork_exec_wait(argv))
1189 g_string_sprintf(message, "!ERROR: Copy failed\n");
1190 send();
1191 retval = FALSE;
1195 return retval;
1198 /* Copy path to dest.
1199 * Check that path not copied into itself.
1201 static gboolean do_copy(char *path, char *dest)
1203 if (is_sub_dir(make_dest_path(path, dest), path))
1205 g_string_sprintf(message,
1206 "!ERROR: Can't copy directory into itself\n");
1207 send();
1208 return FALSE;
1210 return do_copy2(path, dest);
1213 /* Move path to dest.
1214 * Check that path not moved into itself.
1216 static gboolean do_move(char *path, char *dest)
1218 char *dest_path;
1219 char *leaf;
1220 gboolean retval = TRUE;
1221 char *argv[] = {"mv", "-f", NULL, NULL, NULL};
1222 struct stat info2;
1223 gboolean is_dir;
1225 if (is_sub_dir(dest, path))
1227 g_string_sprintf(message,
1228 "!ERROR: Can't move directory into itself\n");
1229 send();
1230 return FALSE;
1233 check_flags();
1235 leaf = strrchr(path, '/');
1236 if (!leaf)
1237 leaf = path; /* Error? */
1238 else
1239 leaf++;
1241 dest_path = make_path(dest, leaf)->str;
1243 is_dir = lstat(path, &info2) == 0 && S_ISDIR(info2.st_mode);
1245 if (access(dest_path, F_OK) == 0)
1247 struct stat info;
1248 int err;
1250 g_string_sprintf(message, "?'%s' already exists - overwrite?",
1251 dest_path);
1252 if (!reply(from_parent, TRUE))
1253 return FALSE;
1255 if (lstat(dest_path, &info))
1257 send_error();
1258 return FALSE;
1261 if (S_ISDIR(info.st_mode))
1262 err = rmdir(dest_path);
1263 else
1264 err = unlink(dest_path);
1266 if (err)
1268 send_error();
1269 if (errno != ENOENT)
1270 return FALSE;
1271 g_string_sprintf(message,
1272 "'Trying move anyway...\n");
1273 send();
1276 else if (!quiet)
1278 g_string_sprintf(message, "?Move %s as %s?", path, dest_path);
1279 if (!reply(from_parent, FALSE))
1280 return FALSE;
1282 else
1284 g_string_sprintf(message, "'Moving %s as %s\n", path,
1285 dest_path);
1286 send();
1289 argv[2] = path;
1290 argv[3] = dest;
1292 if (fork_exec_wait(argv) == 0)
1294 g_string_sprintf(message, "+%s", path);
1295 g_string_truncate(message, leaf - path);
1296 send();
1297 if (is_dir) {
1298 g_string_sprintf(message, "m%s", path);
1299 send();
1302 else
1304 g_string_sprintf(message, "!ERROR: Failed to move %s as %s\n",
1305 path, dest_path);
1306 send();
1307 retval = FALSE;
1310 return retval;
1313 static gboolean do_link(char *path, char *dest)
1315 char *dest_path;
1316 char *leaf;
1318 check_flags();
1320 leaf = strrchr(path, '/');
1321 if (!leaf)
1322 leaf = path; /* Error? */
1323 else
1324 leaf++;
1326 dest_path = make_path(dest, leaf)->str;
1328 if (quiet)
1330 g_string_sprintf(message, "'Linking %s as %s\n", path,
1331 dest_path);
1332 send();
1334 else
1336 g_string_sprintf(message, "?Link %s as %s?", path, dest_path);
1337 if (!reply(from_parent, FALSE))
1338 return FALSE;
1341 if (symlink(path, dest_path))
1343 send_error();
1344 return FALSE;
1347 return TRUE;
1350 /* Mount/umount this item */
1351 static void do_mount(FilerWindow *filer_window, DirItem *item)
1353 char *argv[3] = {NULL, NULL, NULL};
1354 char *action;
1356 check_flags();
1358 argv[0] = item->flags & ITEM_FLAG_MOUNTED ? "umount" : "mount";
1359 action = item->flags & ITEM_FLAG_MOUNTED ? "Unmount" : "Mount";
1360 argv[1] = make_path(filer_window->path, item->leafname)->str;
1362 if (quiet)
1364 g_string_sprintf(message, "'%sing %s\n", action, argv[1]);
1365 send();
1367 else
1369 g_string_sprintf(message, "?%s %s?", action, argv[1]);
1370 if (!reply(from_parent, FALSE))
1371 return;
1374 if (fork_exec_wait(argv) == 0)
1376 g_string_sprintf(message, "+%s", filer_window->path);
1377 send();
1378 g_string_sprintf(message, "m%s", argv[1]);
1379 send();
1381 else
1383 g_string_sprintf(message, "!ERROR: %s failed\n", action);
1384 send();
1388 /* CHILD MAIN LOOPS */
1390 /* After forking, the child calls one of these functions */
1392 static void usage_cb(gpointer data)
1394 FilerWindow *filer_window = (FilerWindow *) data;
1395 Collection *collection = filer_window->collection;
1396 DirItem *item;
1397 int left = collection->number_selected;
1398 int i = -1;
1399 off_t total_size = 0;
1401 send_dir(filer_window->path);
1403 while (left > 0)
1405 i++;
1406 if (!collection->items[i].selected)
1407 continue;
1408 item = (DirItem *) collection->items[i].data;
1409 size_tally = 0;
1410 do_usage(make_path(filer_window->path,
1411 item->leafname)->str,
1412 filer_window->path);
1413 g_string_sprintf(message, "'%s: %s\n",
1414 item->leafname,
1415 format_size((unsigned long) size_tally));
1416 send();
1417 total_size += size_tally;
1418 left--;
1421 g_string_sprintf(message, "'\nTotal: %s\n",
1422 format_size((unsigned long) total_size));
1423 send();
1426 #ifdef DO_MOUNT_POINTS
1427 static void mount_cb(gpointer data)
1429 FilerWindow *filer_window = (FilerWindow *) data;
1430 Collection *collection = filer_window->collection;
1431 DirItem *item;
1432 int i;
1433 gboolean mount_points = FALSE;
1435 send_dir(filer_window->path);
1437 if (mount_item)
1438 do_mount(filer_window, mount_item);
1439 else
1441 for (i = 0; i < collection->number_of_items; i++)
1443 if (!collection->items[i].selected)
1444 continue;
1445 item = (DirItem *) collection->items[i].data;
1446 if (!(item->flags & ITEM_FLAG_MOUNT_POINT))
1447 continue;
1448 mount_points = TRUE;
1450 do_mount(filer_window, item);
1453 if (!mount_points)
1455 g_string_sprintf(message,
1456 "!No mount points selected!\n");
1457 send();
1461 g_string_sprintf(message, "'\nDone\n");
1462 send();
1464 #endif
1466 static void delete_cb(gpointer data)
1468 FilerWindow *filer_window = (FilerWindow *) data;
1469 Collection *collection = filer_window->collection;
1470 DirItem *item;
1471 int left = collection->number_selected;
1472 int i = -1;
1474 send_dir(filer_window->path);
1476 while (left > 0)
1478 i++;
1479 if (!collection->items[i].selected)
1480 continue;
1481 item = (DirItem *) collection->items[i].data;
1482 if (do_delete(make_path(filer_window->path,
1483 item->leafname)->str,
1484 filer_window->path))
1486 g_string_sprintf(message, "+%s", filer_window->path);
1487 send();
1489 left--;
1492 g_string_sprintf(message, "'\nDone\n");
1493 send();
1496 static void chmod_cb(gpointer data)
1498 FilerWindow *filer_window = (FilerWindow *) data;
1499 Collection *collection = filer_window->collection;
1500 DirItem *item;
1501 int left = collection->number_selected;
1502 int i = -1;
1504 send_dir(filer_window->path);
1506 mode_change = mode_compile(chmod_command, MODE_MASK_ALL);
1508 while (left > 0)
1510 i++;
1511 if (!collection->items[i].selected)
1512 continue;
1513 item = (DirItem *) collection->items[i].data;
1514 if (item->flags & ITEM_FLAG_SYMLINK)
1516 g_string_sprintf(message, "!'%s' is a symbolic link\n",
1517 item->leafname);
1518 send();
1520 else if (do_chmod(make_path(filer_window->path,
1521 item->leafname)->str, NULL))
1523 g_string_sprintf(message, "+%s", filer_window->path);
1524 send();
1526 left--;
1529 g_string_sprintf(message, "'\nDone\n");
1530 send();
1533 static void list_cb(gpointer data)
1535 GSList *paths = (GSList *) data;
1537 while (paths)
1539 send_dir((char *) paths->data);
1541 if (action_do_func((char *) paths->data, action_dest))
1543 g_string_sprintf(message, "+%s", action_dest);
1544 send();
1547 paths = paths->next;
1550 g_string_sprintf(message, "'\nDone\n");
1551 send();
1554 static void add_toggle(GUIside *gui_side, guchar *label, guchar *code)
1556 GtkWidget *check;
1558 check = gtk_check_button_new_with_label(label);
1559 gtk_object_set_data(GTK_OBJECT(check), "send-code", code);
1560 gtk_signal_connect(GTK_OBJECT(check), "clicked",
1561 button_reply, gui_side);
1562 gtk_box_pack_start(GTK_BOX(gui_side->vbox), check, FALSE, TRUE, 0);
1566 /* EXTERNAL INTERFACE */
1568 /* Count disk space used by selected items */
1569 void action_usage(FilerWindow *filer_window)
1571 GUIside *gui_side;
1572 Collection *collection;
1574 collection = filer_window->collection;
1576 if (collection->number_selected < 1)
1578 report_error("ROX-Filer", "You need to select some items "
1579 "to count");
1580 return;
1583 gui_side = start_action(filer_window, usage_cb, TRUE);
1584 if (!gui_side)
1585 return;
1587 gui_side->show_info = TRUE;
1589 gtk_window_set_title(GTK_WINDOW(gui_side->window), "Disk Usage");
1590 number_of_windows++;
1591 gtk_widget_show_all(gui_side->window);
1594 /* Mount/unmount 'item', or all selected mount points if NULL. */
1595 void action_mount(FilerWindow *filer_window, DirItem *item)
1597 #ifdef DO_MOUNT_POINTS
1598 GUIside *gui_side;
1599 Collection *collection;
1601 collection = filer_window->collection;
1603 if (item == NULL && collection->number_selected < 1)
1605 report_error("ROX-Filer", "You need to select some items "
1606 "to mount or unmount");
1607 return;
1610 mount_item = item;
1611 gui_side = start_action(filer_window, mount_cb, o_auto_mount);
1612 if (!gui_side)
1613 return;
1615 gtk_window_set_title(GTK_WINDOW(gui_side->window), "Mount / Unmount");
1616 number_of_windows++;
1617 gtk_widget_show_all(gui_side->window);
1618 #else
1619 report_error("ROX-Filer",
1620 "ROX-Filer does not yet support mount points on your "
1621 "system. Sorry.");
1622 #endif /* DO_MOUNT_POINTS */
1625 /* Deletes all selected items in the window */
1626 void action_delete(FilerWindow *filer_window)
1628 GUIside *gui_side;
1629 Collection *collection;
1631 collection = filer_window->collection;
1633 if (collection->number_selected < 1)
1635 report_error("ROX-Filer", "You need to select some items "
1636 "to delete");
1637 return;
1640 gui_side = start_action(filer_window, delete_cb, o_auto_delete);
1641 if (!gui_side)
1642 return;
1644 gtk_window_set_title(GTK_WINDOW(gui_side->window), "Delete");
1645 add_toggle(gui_side,
1646 "Force - don't confirm deletion of non-writeable items", "F");
1647 add_toggle(gui_side,
1648 "Brief - only log directories being deleted", "B");
1650 number_of_windows++;
1651 gtk_widget_show_all(gui_side->window);
1654 /* Change the permissions of the selected items */
1655 void action_chmod(FilerWindow *filer_window)
1657 GUIside *gui_side;
1658 Collection *collection;
1659 GtkWidget *hbox, *label, *button;
1661 collection = filer_window->collection;
1663 if (collection->number_selected < 1)
1665 report_error("ROX-Filer", "You need to select the items "
1666 "whose permissions you want to change");
1667 return;
1670 if (!chmod_command)
1671 chmod_command = g_strdup("a+x");
1672 gui_side = start_action(filer_window, chmod_cb, FALSE);
1673 if (!gui_side)
1674 return;
1676 gtk_window_set_title(GTK_WINDOW(gui_side->window), "Permissions");
1677 add_toggle(gui_side,
1678 "Brief - don't list processed files", "B");
1679 add_toggle(gui_side,
1680 "Recurse - also change contents of subdirectories", "R");
1681 hbox = gtk_hbox_new(FALSE, 0);
1682 label = gtk_label_new("Command:");
1683 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, TRUE, 4);
1684 gui_side->entry = gtk_entry_new();
1685 gtk_entry_set_text(GTK_ENTRY(gui_side->entry), chmod_command);
1686 gtk_widget_set_sensitive(gui_side->entry, FALSE);
1687 gtk_box_pack_start(GTK_BOX(hbox), gui_side->entry, TRUE, TRUE, 4);
1688 gtk_signal_connect(GTK_OBJECT(gui_side->entry), "changed",
1689 chmod_command_changed, gui_side);
1690 gtk_box_pack_start(GTK_BOX(gui_side->vbox), hbox, FALSE, TRUE, 0);
1691 button = gtk_button_new_with_label("Show command reference");
1692 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, TRUE, 4);
1693 gtk_signal_connect_object(GTK_OBJECT(button), "clicked",
1694 show_chmod_help, NULL);
1696 number_of_windows++;
1697 gtk_widget_show_all(gui_side->window);
1700 void action_copy(GSList *paths, char *dest, char *leaf)
1702 GUIside *gui_side;
1704 action_dest = dest;
1705 action_leaf = leaf;
1706 action_do_func = do_copy;
1707 gui_side = start_action(paths, list_cb, o_auto_copy);
1708 if (!gui_side)
1709 return;
1711 gtk_window_set_title(GTK_WINDOW(gui_side->window), "Copy");
1712 number_of_windows++;
1713 gtk_widget_show_all(gui_side->window);
1716 void action_move(GSList *paths, char *dest)
1718 GUIside *gui_side;
1720 action_dest = dest;
1721 action_do_func = do_move;
1722 gui_side = start_action(paths, list_cb, o_auto_move);
1723 if (!gui_side)
1724 return;
1726 gtk_window_set_title(GTK_WINDOW(gui_side->window), "Move");
1727 number_of_windows++;
1728 gtk_widget_show_all(gui_side->window);
1731 void action_link(GSList *paths, char *dest)
1733 GUIside *gui_side;
1735 action_dest = dest;
1736 action_do_func = do_link;
1737 gui_side = start_action(paths, list_cb, o_auto_link);
1738 if (!gui_side)
1739 return;
1741 gtk_window_set_title(GTK_WINDOW(gui_side->window), "Link");
1742 number_of_windows++;
1743 gtk_widget_show_all(gui_side->window);
1746 void action_init(void)
1748 options_sections = g_slist_prepend(options_sections, &options);
1749 option_register("action_auto_quiet", action_auto_quiet);