r246: Added support for i18n. No translations yet, though!
[rox-filer/ma.git] / ROX-Filer / src / action.c
blob92c531cd9077ba4d44aacc724c762e34a4bbc565
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"
48 #include "find.h"
50 /* Options bits */
51 static GtkWidget *create_options();
52 static void update_options();
53 static void set_options();
54 static void save_options();
55 static char *action_auto_quiet(char *data);
57 static OptionsSection options =
59 N_("Action window options"),
60 create_options,
61 update_options,
62 set_options,
63 save_options
66 static gboolean o_auto_copy = TRUE;
67 static gboolean o_auto_move = TRUE;
68 static gboolean o_auto_link = TRUE;
69 static gboolean o_auto_delete = FALSE;
70 static gboolean o_auto_mount = TRUE;
72 static GtkWidget *w_auto_copy = NULL;
73 static GtkWidget *w_auto_move = NULL;
74 static GtkWidget *w_auto_link = NULL;
75 static GtkWidget *w_auto_delete = NULL;
76 static GtkWidget *w_auto_mount = NULL;
78 static GdkColor red = {0, 0xffff, 0, 0};
80 /* Parent->Child messages are one character each:
82 * Q/Y/N Quiet/Yes/No button clicked
83 * F Force deletion of non-writeable items
86 #define SENSITIVE_YESNO(gui_side, state) \
87 do { \
88 gtk_widget_set_sensitive((gui_side)->yes, state); \
89 gtk_widget_set_sensitive((gui_side)->no, state); \
90 if ((gui_side)->entry) \
91 gtk_widget_set_sensitive((gui_side)->entry, state);\
92 } while (0)
94 #define ON(flag) ((flag) ? "on" : "off")
96 typedef struct _GUIside GUIside;
97 typedef void ActionChild(gpointer data);
98 typedef gboolean ForDirCB(char *path, char *dest_path);
100 struct _GUIside
102 int from_child; /* File descriptor */
103 FILE *to_child;
104 int input_tag; /* gdk_input_add() */
105 GtkWidget *vbox, *log, *window, *dir, *log_hbox;
106 GtkWidget *quiet, *yes, *no;
107 int child; /* Process ID */
108 int errors;
109 gboolean show_info; /* For Disk Usage */
111 GtkWidget *entry; /* May be NULL */
112 guchar **default_string; /* Changed when the entry changes */
114 char *next_dir; /* NULL => no timer active */
115 gint next_timer;
117 /* Used by Find */
118 FilerWindow *preview;
119 GtkWidget *results;
122 /* These don't need to be in a structure because we fork() before
123 * using them again.
125 static int from_parent = 0;
126 static FILE *to_parent = NULL;
127 static gboolean quiet = FALSE;
128 static GString *message = NULL;
129 static char *action_dest = NULL;
130 static char *action_leaf = NULL;
131 static gboolean (*action_do_func)(char *source, char *dest);
132 static size_t size_tally; /* For Disk Usage */
133 static DirItem *mount_item;
135 static struct mode_change *mode_change = NULL; /* For Permissions */
136 static FindCondition *find_condition = NULL; /* For Find */
138 static gboolean o_force = FALSE;
139 static gboolean o_brief = FALSE;
140 static gboolean o_recurse = FALSE;
142 /* Whenever the text in these boxes is changed we store a copy of the new
143 * string to be used as the default next time.
145 static guchar *last_chmod_string = NULL;
146 static guchar *last_find_string = NULL;
148 /* Set to one of the above before forking. This may change over a call to
149 * reply(). It is reset to NULL once the text is parsed.
151 static guchar *new_entry_string = NULL;
153 /* Static prototypes */
154 static gboolean send();
155 static gboolean send_error();
156 static gboolean send_dir(char *dir);
157 static gboolean read_exact(int source, char *buffer, ssize_t len);
158 static void do_mount(FilerWindow *filer_window, DirItem *item);
159 static void add_toggle(GUIside *gui_side, guchar *label, guchar *code);
160 static gboolean reply(int fd, gboolean ignore_quiet);
162 /* OPTIONS */
164 /* Build up some option widgets to go in the options dialog, but don't
165 * fill them in yet.
167 static GtkWidget *create_options()
169 GtkWidget *vbox, *hbox, *label;
171 vbox = gtk_vbox_new(FALSE, 0);
172 gtk_container_set_border_width(GTK_CONTAINER(vbox), 4);
174 label = gtk_label_new("Auto-start (Quiet) these actions:");
175 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, TRUE, 0);
177 hbox = gtk_hbox_new(TRUE, 0);
178 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0);
180 w_auto_copy = gtk_check_button_new_with_label("Copy");
181 gtk_box_pack_start(GTK_BOX(hbox), w_auto_copy, FALSE, TRUE, 0);
182 w_auto_move = gtk_check_button_new_with_label("Move");
183 gtk_box_pack_start(GTK_BOX(hbox), w_auto_move, FALSE, TRUE, 0);
184 w_auto_link = gtk_check_button_new_with_label("Link");
185 gtk_box_pack_start(GTK_BOX(hbox), w_auto_link, FALSE, TRUE, 0);
186 w_auto_delete = gtk_check_button_new_with_label("Delete");
187 gtk_box_pack_start(GTK_BOX(hbox), w_auto_delete, FALSE, TRUE, 0);
188 w_auto_mount = gtk_check_button_new_with_label("Mount");
189 gtk_box_pack_start(GTK_BOX(hbox), w_auto_mount, FALSE, TRUE, 0);
191 return vbox;
194 /* Reflect current state by changing the widgets in the options box */
195 static void update_options()
197 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w_auto_copy),
198 o_auto_copy);
199 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w_auto_move),
200 o_auto_move);
201 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w_auto_link),
202 o_auto_link);
203 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w_auto_delete),
204 o_auto_delete);
205 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w_auto_mount),
206 o_auto_mount);
209 /* Set current values by reading the states of the widgets in the options box */
210 static void set_options()
212 o_auto_copy = gtk_toggle_button_get_active(
213 GTK_TOGGLE_BUTTON(w_auto_copy));
214 o_auto_move = gtk_toggle_button_get_active(
215 GTK_TOGGLE_BUTTON(w_auto_move));
216 o_auto_link = gtk_toggle_button_get_active(
217 GTK_TOGGLE_BUTTON(w_auto_link));
218 o_auto_delete = gtk_toggle_button_get_active(
219 GTK_TOGGLE_BUTTON(w_auto_delete));
220 o_auto_mount = gtk_toggle_button_get_active(
221 GTK_TOGGLE_BUTTON(w_auto_mount));
224 static void save_options()
226 guchar str[] = "cmldt";
228 if (o_auto_copy)
229 str[0] = 'C';
230 if (o_auto_move)
231 str[1] = 'M';
232 if (o_auto_link)
233 str[2] = 'L';
234 if (o_auto_delete)
235 str[3] = 'D';
236 if (o_auto_mount)
237 str[4] = 'T';
239 option_write("action_auto_quiet", str);
242 static char *action_auto_quiet(char *data)
244 while (*data)
246 char c = *data++;
247 gboolean state;
249 state = isupper(c);
251 switch (tolower(c))
253 case 'c':
254 o_auto_copy = state;
255 break;
256 case 'm':
257 o_auto_move = state;
258 break;
259 case 'l':
260 o_auto_link = state;
261 break;
262 case 'd':
263 o_auto_delete = state;
264 break;
265 case 't':
266 o_auto_mount = state;
267 break;
268 default:
269 return "Unknown flag";
273 return NULL;
276 /* SUPPORT */
278 static void preview_closed(GtkWidget *window, GUIside *gui_side)
280 gui_side->preview = NULL;
283 static void select_row_callback(GtkWidget *widget,
284 gint row, gint column,
285 GdkEventButton *event,
286 GUIside *gui_side)
288 gchar *leaf, *dir;
290 gtk_clist_get_text(GTK_CLIST(gui_side->results), row, 0, &leaf);
291 gtk_clist_get_text(GTK_CLIST(gui_side->results), row, 1, &dir);
293 gtk_clist_unselect_row(GTK_CLIST(gui_side->results), row, column);
295 if (gui_side->preview)
297 if (strcmp(gui_side->preview->path, dir) == 0)
298 filer_set_autoselect(gui_side->preview, leaf);
299 else
300 filer_change_to(gui_side->preview, dir, leaf);
301 return;
304 gui_side->preview = filer_opendir(dir, PANEL_NO);
305 filer_set_autoselect(gui_side->preview, leaf);
306 gtk_signal_connect(GTK_OBJECT(gui_side->preview->window), "destroy",
307 GTK_SIGNAL_FUNC(preview_closed), gui_side);
311 /* This is called whenever the user edits the entry box (if any) - send the
312 * new string.
314 static void entry_changed(GtkEntry *entry, GUIside *gui_side)
316 guchar *text;
318 g_return_if_fail(gui_side->default_string != NULL);
320 text = gtk_entry_get_text(entry);
322 g_free(*(gui_side->default_string));
323 *(gui_side->default_string) = g_strdup(text);
325 if (!gui_side->to_child)
326 return;
328 fputc('E', gui_side->to_child);
329 fputs(text, gui_side->to_child);
330 fputc('\n', gui_side->to_child);
331 fflush(gui_side->to_child);
334 static void show_condition_help(gpointer data)
336 static GtkWidget *help = NULL;
338 if (!help)
340 GtkWidget *text, *vbox, *button, *hbox, *frame;
342 help = gtk_window_new(GTK_WINDOW_DIALOG);
343 gtk_container_set_border_width(GTK_CONTAINER(help), 10);
344 gtk_window_set_title(GTK_WINDOW(help),
345 "Find expression reference");
347 vbox = gtk_vbox_new(FALSE, 0);
348 gtk_container_add(GTK_CONTAINER(help), vbox);
350 frame = gtk_frame_new("Quick Start");
351 text = gtk_label_new(
352 "Just put the name of the file you're looking for in single quotes:\n"
353 "'index.html' (to find a file called 'index.html')");
354 gtk_misc_set_padding(GTK_MISC(text), 4, 4);
355 gtk_label_set_justify(GTK_LABEL(text), GTK_JUSTIFY_LEFT);
356 gtk_container_add(GTK_CONTAINER(frame), text);
357 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 4);
359 frame = gtk_frame_new("Examples");
360 text = gtk_label_new(
361 "'*.htm', '*.html' (finds HTML files)\n"
362 "IsDir 'lib' (finds directories called 'lib')\n"
363 "IsReg 'core' (finds a regular file called 'core')\n"
364 "! (IsDir, IsReg) (is neither a directory nor a regualr file)\n"
365 "mtime after 1 day ago and size > 1Mb (big, and recently modified)\n"
366 "'CVS' prune, isreg (a regular file not in CVS)\n"
367 "IsReg system(grep -q fred \"%\") (contains the word 'fred')");
368 gtk_widget_set_style(text, fixed_style);
369 gtk_misc_set_padding(GTK_MISC(text), 4, 4);
370 gtk_label_set_justify(GTK_LABEL(text), GTK_JUSTIFY_LEFT);
371 gtk_container_add(GTK_CONTAINER(frame), text);
372 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 4);
374 frame = gtk_frame_new("Simple Tests");
375 text = gtk_label_new(
376 "IsReg, IsLink, IsDir, IsChar, IsBlock, IsDev, IsPipe, IsSocket (types)\n"
377 "IsSUID, IsSGID, IsSticky, IsReadable, IsWriteable, IsExecutable (permissions)"
378 "\n"
379 "IsEmpty, IsMine\n"
380 "\n"
381 "A pattern in single quotes is a shell-style wildcard pattern to match. If it\n"
382 "contains a slash then the match is agaist the full path; otherwise it is \n"
383 "agaist the leafname only.");
384 gtk_misc_set_padding(GTK_MISC(text), 4, 4);
385 gtk_label_set_justify(GTK_LABEL(text), GTK_JUSTIFY_LEFT);
386 gtk_container_add(GTK_CONTAINER(frame), text);
387 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 4);
389 frame = gtk_frame_new("Comparisons");
390 text = gtk_label_new(
391 "<, <=, =, !=, >, >=, After, Before (compare two values)\n"
392 "5 bytes, 1Kb, 2Mb, 3Gb (file sizes)\n"
393 "2 secs|mins|hours|days|weeks|years ago|hence (times)\n"
394 "atime, ctime, mtime, now, size, inode, nlinks, uid, gid, blocks (values)");
395 gtk_misc_set_padding(GTK_MISC(text), 4, 4);
396 gtk_label_set_justify(GTK_LABEL(text), GTK_JUSTIFY_LEFT);
397 gtk_container_add(GTK_CONTAINER(frame), text);
398 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 4);
400 frame = gtk_frame_new("Specials");
401 text = gtk_label_new(
402 "system(command) (true if 'command' returns with a zero exit status; a % \n"
403 "in 'command' is replaced with the path of the current file)\n"
404 "prune (false, and prevents searching the contents of a directory)."
406 gtk_misc_set_padding(GTK_MISC(text), 4, 4);
407 gtk_label_set_justify(GTK_LABEL(text), GTK_JUSTIFY_LEFT);
408 gtk_container_add(GTK_CONTAINER(frame), text);
409 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 4);
411 hbox = gtk_hbox_new(FALSE, 20);
412 gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 0);
414 text = gtk_label_new(
415 "See the ROX-Filer manual for full details.");
416 gtk_box_pack_start(GTK_BOX(hbox), text, TRUE, TRUE, 0);
417 button = gtk_button_new_with_label("Close");
418 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, TRUE, 0);
419 gtk_signal_connect_object(GTK_OBJECT(button), "clicked",
420 gtk_widget_hide, GTK_OBJECT(help));
422 gtk_signal_connect_object(GTK_OBJECT(help), "delete_event",
423 gtk_widget_hide, GTK_OBJECT(help));
426 if (GTK_WIDGET_VISIBLE(help))
427 gtk_widget_hide(help);
428 gtk_widget_show_all(help);
431 static void show_chmod_help(gpointer data)
433 static GtkWidget *help = NULL;
435 if (!help)
437 GtkWidget *text, *vbox, *button, *hbox, *sep;
439 help = gtk_window_new(GTK_WINDOW_DIALOG);
440 gtk_container_set_border_width(GTK_CONTAINER(help), 10);
441 gtk_window_set_title(GTK_WINDOW(help),
442 "Permissions command reference");
444 vbox = gtk_vbox_new(FALSE, 0);
445 gtk_container_add(GTK_CONTAINER(help), vbox);
447 text = gtk_label_new(
448 "The format of a command is:\n"
449 "CHANGE, CHANGE, ...\n"
450 "Each CHANGE is:\n"
451 "WHO HOW PERMISSIONS\n"
452 "WHO is some combination of u, g and o which determines whether to\n"
453 "change the permissions for the User (owner), Group or Others.\n"
454 "HOW is +, - or = to add, remove or set exactly the permissions.\n"
455 "PERMISSIONS is some combination of the letters 'rwxXstugo'\n\n"
457 "Examples:\n"
458 "u+rw (the file owner gains read and write permission)\n"
459 "g=u (the group permissions are set to be the same as the user's)\n"
460 "o=u-w (others get the same permissions as the owner, but without "
461 "write permission)\n"
462 "a+x (everyone gets execute/access permission - same as 'ugo+x')\n"
463 "a+X (directories become accessable by everyone; files which were\n"
464 "executable by anyone become executable by everyone)\n"
465 "u+rw, go+r (two commands at once!)\n"
466 "u+s (set the SetUID bit - often has no effect on script files)\n"
467 "755 (set the permissions directly)\n"
469 "\nSee the chmod(1) man page for full details.");
470 gtk_label_set_justify(GTK_LABEL(text), GTK_JUSTIFY_LEFT);
471 gtk_box_pack_start(GTK_BOX(vbox), text, TRUE, TRUE, 0);
473 hbox = gtk_hbox_new(FALSE, 20);
474 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0);
476 sep = gtk_hseparator_new();
477 gtk_box_pack_start(GTK_BOX(hbox), sep, TRUE, TRUE, 0);
478 button = gtk_button_new_with_label("Close");
479 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, TRUE, 0);
480 gtk_signal_connect_object(GTK_OBJECT(button), "clicked",
481 gtk_widget_hide, GTK_OBJECT(help));
483 gtk_signal_connect_object(GTK_OBJECT(help), "delete_event",
484 gtk_widget_hide, GTK_OBJECT(help));
487 if (GTK_WIDGET_VISIBLE(help))
488 gtk_widget_hide(help);
489 gtk_widget_show_all(help);
492 /* TRUE iff `sub' is (or would be) an object inside the directory `parent',
493 * (or the two are the same directory)
495 static gboolean is_sub_dir(char *sub, char *parent)
497 int parent_len;
498 guchar *real_sub, *real_parent;
499 gboolean retval;
501 real_sub = pathdup(sub);
502 real_parent = pathdup(parent);
504 parent_len = strlen(real_parent);
505 if (strncmp(real_parent, real_sub, parent_len))
506 retval = FALSE;
507 else
509 /* real_sub is at least as long as real_parent and all
510 * characters upto real_parent's length match.
513 retval = real_sub[parent_len] == '\0' ||
514 real_sub[parent_len] == '/';
517 g_free(real_sub);
518 g_free(real_parent);
520 return retval;
523 static gboolean display_dir(gpointer data)
525 GUIside *gui_side = (GUIside *) data;
527 gtk_label_set_text(GTK_LABEL(gui_side->dir), gui_side->next_dir + 1);
528 g_free(gui_side->next_dir);
529 gui_side->next_dir = NULL;
531 return FALSE;
534 static void add_to_results(GUIside *gui_side, gchar *path)
536 gchar *row[] = {"Leaf", "Dir"};
537 gchar *slash;
538 int len;
540 slash = strrchr(path, '/');
541 g_return_if_fail(slash != NULL);
543 len = slash - path;
544 row[1] = g_strndup(path, MAX(len, 1));
545 row[0] = slash + 1;
547 gtk_clist_append(GTK_CLIST(gui_side->results), row);
549 g_free(row[1]);
552 /* Called when the child sends us a message */
553 static void message_from_child(gpointer data,
554 gint source,
555 GdkInputCondition condition)
557 char buf[5];
558 GUIside *gui_side = (GUIside *) data;
559 GtkWidget *log = gui_side->log;
561 if (read_exact(source, buf, 4))
563 ssize_t message_len;
564 char *buffer;
566 buf[4] = '\0';
567 message_len = strtol(buf, NULL, 16);
568 buffer = g_malloc(message_len + 1);
569 if (message_len > 0 && read_exact(source, buffer, message_len))
571 buffer[message_len] = '\0';
572 if (*buffer == '?')
574 SENSITIVE_YESNO(gui_side, TRUE);
575 gtk_window_set_focus(
576 GTK_WINDOW(gui_side->window),
577 gui_side->entry ? gui_side->entry
578 : gui_side->yes);
580 else if (*buffer == '+')
582 refresh_dirs(buffer + 1);
583 g_free(buffer);
584 return;
586 else if (*buffer == '=')
588 add_to_results(gui_side, buffer + 1);
589 g_free(buffer);
590 return;
592 else if (*buffer == 'm')
594 filer_check_mounted(buffer + 1);
595 g_free(buffer);
596 return;
598 else if (*buffer == '/')
600 if (gui_side->next_dir)
601 g_free(gui_side->next_dir);
602 else
603 gui_side->next_timer =
604 gtk_timeout_add(500,
605 display_dir,
606 gui_side);
607 gui_side->next_dir = buffer;
608 return;
610 else if (*buffer == '!')
611 gui_side->errors++;
613 gtk_text_insert(GTK_TEXT(log),
614 NULL,
615 *buffer == '!' ? &red : NULL,
616 NULL,
617 buffer + 1, message_len - 1);
618 g_free(buffer);
619 return;
621 g_print("Child died in the middle of a message.\n");
624 /* The child is dead */
625 gui_side->child = 0;
627 fclose(gui_side->to_child);
628 gui_side->to_child = NULL;
629 close(gui_side->from_child);
630 gdk_input_remove(gui_side->input_tag);
631 gtk_widget_set_sensitive(gui_side->quiet, FALSE);
633 if (gui_side->errors)
635 GString *report;
636 report = g_string_new(NULL);
637 g_string_sprintf(report, "There %s %d error%s.\n",
638 gui_side->errors == 1 ? "was" : "were",
639 gui_side->errors,
640 gui_side->errors == 1 ? "" : "s");
641 gtk_text_insert(GTK_TEXT(log), NULL, &red, NULL,
642 report->str, report->len);
644 g_string_free(report, TRUE);
646 else if (gui_side->show_info == FALSE)
647 gtk_widget_destroy(gui_side->window);
650 /* Scans src_dir, updating dest_path whenever cb returns TRUE */
651 static void for_dir_contents(ForDirCB *cb, char *src_dir, char *dest_path)
653 DIR *d;
654 struct dirent *ent;
655 GSList *list = NULL, *next;
657 d = mc_opendir(src_dir);
658 if (!d)
660 g_string_sprintf(message, "!ERROR reading '%s': %s\n",
661 src_dir, g_strerror(errno));
662 send();
663 return;
666 send_dir(src_dir);
668 while ((ent = mc_readdir(d)))
670 if (ent->d_name[0] == '.' && (ent->d_name[1] == '\0'
671 || (ent->d_name[1] == '.' && ent->d_name[2] == '\0')))
672 continue;
673 list = g_slist_append(list, g_strdup(make_path(src_dir,
674 ent->d_name)->str));
676 mc_closedir(d);
678 if (!list)
679 return;
681 next = list;
683 while (next)
685 if (cb((char *) next->data, dest_path))
687 g_string_sprintf(message, "+%s", dest_path);
688 send();
691 g_free(next->data);
692 next = next->next;
694 g_slist_free(list);
695 return;
698 /* Read this many bytes into the buffer. TRUE on success. */
699 static gboolean read_exact(int source, char *buffer, ssize_t len)
701 while (len > 0)
703 ssize_t got;
704 got = read(source, buffer, len);
705 if (got < 1)
706 return FALSE;
707 len -= got;
708 buffer += got;
710 return TRUE;
713 /* Send 'message' to our parent process. TRUE on success. */
714 static gboolean send()
716 char len_buffer[5];
717 ssize_t len;
719 g_return_val_if_fail(message->len < 0xffff, FALSE);
721 sprintf(len_buffer, "%04x", message->len);
722 fwrite(len_buffer, 1, 4, to_parent);
723 len = fwrite(message->str, 1, message->len, to_parent);
724 fflush(to_parent);
725 return len == message->len;
728 /* Set the directory indicator at the top of the window */
729 static gboolean send_dir(char *dir)
731 g_string_sprintf(message, "/%s", dir);
732 return send();
735 static gboolean send_error()
737 g_string_sprintf(message, "!ERROR: %s\n", g_strerror(errno));
738 return send();
741 static void button_reply(GtkWidget *button, GUIside *gui_side)
743 char *text;
745 if (!gui_side->to_child)
746 return;
748 text = gtk_object_get_data(GTK_OBJECT(button), "send-code");
749 g_return_if_fail(text != NULL);
750 fputc(*text, gui_side->to_child);
751 fflush(gui_side->to_child);
753 if (*text == 'Y' || *text == 'N' || *text == 'Q')
754 SENSITIVE_YESNO(gui_side, FALSE);
757 static void process_flag(char flag)
759 switch (flag)
761 case 'Q':
762 quiet = !quiet;
763 break;
764 case 'F':
765 o_force = !o_force;
766 break;
767 case 'R':
768 o_recurse = !o_recurse;
769 break;
770 case 'B':
771 o_brief = !o_brief;
772 break;
773 default:
774 g_string_sprintf(message,
775 "!ERROR: Bad message '%c'\n", flag);
776 send();
777 break;
781 /* If the parent has sent any flag toggles, read them */
782 static void check_flags(void)
784 fd_set set;
785 int got;
786 char retval;
787 struct timeval tv;
789 FD_ZERO(&set);
791 while (1)
793 FD_SET(from_parent, &set);
794 tv.tv_sec = 0;
795 tv.tv_usec = 0;
796 got = select(from_parent + 1, &set, NULL, NULL, &tv);
798 if (got == -1)
799 g_error("select() failed: %s\n", g_strerror(errno));
800 else if (!got)
801 return;
803 got = read(from_parent, &retval, 1);
804 if (got != 1)
805 g_error("read() error: %s\n", g_strerror(errno));
807 process_flag(retval);
811 static void read_new_entry_text(void)
813 int len;
814 char c;
815 GString *new;
817 new = g_string_new(NULL);
819 for (;;)
821 len = read(from_parent, &c, 1);
822 if (len != 1)
824 fprintf(stderr, "read() error: %s\n",
825 g_strerror(errno));
826 _exit(1); /* Parent died? */
829 if (c == '\n')
830 break;
831 g_string_append_c(new, c);
834 g_free(new_entry_string);
835 new_entry_string = new->str;
836 g_string_free(new, FALSE);
839 /* Read until the user sends a reply. If ignore_quiet is TRUE then
840 * the user MUST click Yes or No, else treat quiet on as Yes.
841 * If the user needs prompting then does send().
843 static gboolean reply(int fd, gboolean ignore_quiet)
845 ssize_t len;
846 char retval;
847 gboolean asked = FALSE;
849 while (ignore_quiet || !quiet)
851 if (!asked)
853 send();
854 asked = TRUE;
857 len = read(fd, &retval, 1);
858 if (len != 1)
860 fprintf(stderr, "read() error: %s\n",
861 g_strerror(errno));
862 _exit(1); /* Parent died? */
865 switch (retval)
867 case 'Q':
868 quiet = !quiet;
869 if (ignore_quiet)
871 g_string_assign(message, "?");
872 send();
874 break;
875 case 'Y':
876 g_string_assign(message, "' Yes\n");
877 send();
878 return TRUE;
879 case 'N':
880 g_string_assign(message, "' No\n");
881 send();
882 return FALSE;
883 case 'E':
884 read_new_entry_text();
885 break;
886 default:
887 process_flag(retval);
888 break;
892 if (asked)
894 g_string_assign(message, "' Quiet\n");
895 send();
897 return TRUE;
900 static void destroy_action_window(GtkWidget *widget, gpointer data)
902 GUIside *gui_side = (GUIside *) data;
904 if (gui_side->child)
906 kill(gui_side->child, SIGTERM);
907 fclose(gui_side->to_child);
908 close(gui_side->from_child);
909 gdk_input_remove(gui_side->input_tag);
912 if (gui_side->next_dir)
914 gtk_timeout_remove(gui_side->next_timer);
915 g_free(gui_side->next_dir);
918 if (gui_side->preview)
920 gtk_signal_disconnect_by_data(
921 GTK_OBJECT(gui_side->preview->window),
922 gui_side);
923 gui_side->preview = NULL;
926 g_free(gui_side);
928 if (--number_of_windows < 1)
929 gtk_main_quit();
932 /* Create two pipes, fork() a child and return a pointer to a GUIside struct
933 * (NULL on failure). The child calls func().
935 * If autoq then automatically selects 'Quiet'.
937 static GUIside *start_action(gpointer data, ActionChild *func, gboolean autoq)
939 int filedes[4]; /* 0 and 2 are for reading */
940 GUIside *gui_side;
941 int child;
942 GtkWidget *vbox, *button, *scrollbar, *actions;
943 struct sigaction act;
945 if (pipe(filedes))
947 report_error("ROX-Filer", g_strerror(errno));
948 return NULL;
951 if (pipe(filedes + 2))
953 close(filedes[0]);
954 close(filedes[1]);
955 report_error("ROX-Filer", g_strerror(errno));
956 return NULL;
959 child = fork();
960 switch (child)
962 case -1:
963 report_error("ROX-Filer", g_strerror(errno));
964 return NULL;
965 case 0:
966 /* We are the child */
968 dup2(to_error_log, STDOUT_FILENO);
969 close_on_exec(STDOUT_FILENO, FALSE);
970 dup2(to_error_log, STDERR_FILENO);
971 close_on_exec(STDERR_FILENO, FALSE);
973 quiet = autoq;
975 /* Reset the SIGCHLD handler */
976 act.sa_handler = SIG_DFL;
977 sigemptyset(&act.sa_mask);
978 act.sa_flags = 0;
979 sigaction(SIGCHLD, &act, NULL);
981 message = g_string_new(NULL);
982 close(filedes[0]);
983 close(filedes[3]);
984 to_parent = fdopen(filedes[1], "wb");
985 from_parent = filedes[2];
986 func(data);
987 _exit(0);
990 /* We are the parent */
991 close(filedes[1]);
992 close(filedes[2]);
993 gui_side = g_malloc(sizeof(GUIside));
994 gui_side->from_child = filedes[0];
995 gui_side->to_child = fdopen(filedes[3], "wb");
996 gui_side->log = NULL;
997 gui_side->child = child;
998 gui_side->errors = 0;
999 gui_side->show_info = FALSE;
1000 gui_side->preview = NULL;
1001 gui_side->results = NULL;
1002 gui_side->entry = NULL;
1003 gui_side->default_string = NULL;
1005 gui_side->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1006 gtk_container_set_border_width(GTK_CONTAINER(gui_side->window), 2);
1007 gtk_window_set_default_size(GTK_WINDOW(gui_side->window), 450, 200);
1008 gtk_signal_connect(GTK_OBJECT(gui_side->window), "destroy",
1009 GTK_SIGNAL_FUNC(destroy_action_window), gui_side);
1011 gui_side->vbox = vbox = gtk_vbox_new(FALSE, 0);
1012 gtk_container_add(GTK_CONTAINER(gui_side->window), vbox);
1014 gui_side->dir = gtk_label_new("<dir>");
1015 gui_side->next_dir = NULL;
1016 gtk_misc_set_alignment(GTK_MISC(gui_side->dir), 0.5, 0.5);
1017 gtk_box_pack_start(GTK_BOX(vbox), gui_side->dir, FALSE, TRUE, 0);
1019 gui_side->log_hbox = gtk_hbox_new(FALSE, 0);
1020 gtk_box_pack_start(GTK_BOX(vbox), gui_side->log_hbox, TRUE, TRUE, 4);
1022 gui_side->log = gtk_text_new(NULL, NULL);
1023 gtk_widget_set_usize(gui_side->log, 400, 100);
1024 gtk_box_pack_start(GTK_BOX(gui_side->log_hbox),
1025 gui_side->log, TRUE, TRUE, 0);
1026 scrollbar = gtk_vscrollbar_new(GTK_TEXT(gui_side->log)->vadj);
1027 gtk_box_pack_start(GTK_BOX(gui_side->log_hbox),
1028 scrollbar, FALSE, TRUE, 0);
1030 actions = gtk_hbox_new(TRUE, 4);
1031 gtk_box_pack_start(GTK_BOX(vbox), actions, FALSE, TRUE, 0);
1033 gui_side->quiet = button = gtk_toggle_button_new_with_label("Quiet");
1034 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), autoq);
1035 gtk_object_set_data(GTK_OBJECT(button), "send-code", "Q");
1036 gtk_box_pack_start(GTK_BOX(actions), button, TRUE, TRUE, 0);
1037 gtk_signal_connect(GTK_OBJECT(button), "clicked",
1038 button_reply, gui_side);
1039 gui_side->yes = button = gtk_button_new_with_label("Yes");
1040 gtk_object_set_data(GTK_OBJECT(button), "send-code", "Y");
1041 gtk_box_pack_start(GTK_BOX(actions), button, TRUE, TRUE, 0);
1042 gtk_signal_connect(GTK_OBJECT(button), "clicked",
1043 button_reply, gui_side);
1044 gui_side->no = button = gtk_button_new_with_label("No");
1045 gtk_object_set_data(GTK_OBJECT(button), "send-code", "N");
1046 gtk_box_pack_start(GTK_BOX(actions), button, TRUE, TRUE, 0);
1047 gtk_signal_connect(GTK_OBJECT(button), "clicked",
1048 button_reply, gui_side);
1049 SENSITIVE_YESNO(gui_side, FALSE);
1051 button = gtk_button_new_with_label("Abort");
1052 gtk_box_pack_start(GTK_BOX(actions), button, TRUE, TRUE, 0);
1053 gtk_signal_connect_object(GTK_OBJECT(button), "clicked",
1054 gtk_widget_destroy, GTK_OBJECT(gui_side->window));
1056 gui_side->input_tag = gdk_input_add(gui_side->from_child,
1057 GDK_INPUT_READ,
1058 message_from_child,
1059 gui_side);
1061 return gui_side;
1064 /* ACTIONS ON ONE ITEM */
1066 /* These may call themselves recursively, or ask questions, etc.
1067 * TRUE iff the directory containing dest_path needs to be rescanned.
1070 /* dest_path is the dir containing src_path.
1071 * Updates the global size_tally.
1073 static gboolean do_usage(char *src_path, char *dest_path)
1075 struct stat info;
1077 check_flags();
1079 if (mc_lstat(src_path, &info))
1081 g_string_sprintf(message, "'%s:\n", src_path);
1082 send();
1083 send_error();
1084 return FALSE;
1087 if (S_ISREG(info.st_mode) || S_ISLNK(info.st_mode))
1088 size_tally += info.st_size;
1089 else if (S_ISDIR(info.st_mode))
1091 g_string_sprintf(message, "?Count contents of %s?",
1092 src_path);
1093 if (reply(from_parent, FALSE))
1095 char *safe_path;
1096 safe_path = g_strdup(src_path);
1097 for_dir_contents(do_usage, safe_path, safe_path);
1098 g_free(safe_path);
1102 return FALSE;
1105 /* dest_path is the dir containing src_path */
1106 static gboolean do_delete(char *src_path, char *dest_path)
1108 struct stat info;
1109 gboolean write_prot;
1111 check_flags();
1113 if (mc_lstat(src_path, &info))
1115 send_error();
1116 return FALSE;
1119 write_prot = S_ISLNK(info.st_mode) ? FALSE
1120 : access(src_path, W_OK) != 0;
1121 if (write_prot || !quiet)
1123 g_string_sprintf(message, "?Delete %s'%s'?",
1124 write_prot ? "WRITE-PROTECTED " : " ",
1125 src_path);
1126 if (!reply(from_parent, write_prot && !o_force))
1127 return FALSE;
1129 else if (!o_brief)
1131 g_string_sprintf(message, "'Deleting '%s'\n", src_path);
1132 send();
1135 if (S_ISDIR(info.st_mode))
1137 char *safe_path;
1138 safe_path = g_strdup(src_path);
1139 for_dir_contents(do_delete, safe_path, safe_path);
1140 if (rmdir(safe_path))
1142 g_free(safe_path);
1143 send_error();
1144 return FALSE;
1146 g_string_sprintf(message, "'Directory '%s' deleted\n",
1147 safe_path);
1148 send();
1149 g_string_sprintf(message, "m%s", safe_path);
1150 send();
1151 g_free(safe_path);
1153 else if (unlink(src_path))
1155 send_error();
1156 return FALSE;
1159 return TRUE;
1162 /* path is the item to check. If is is a directory then we may recurse
1163 * (unless prune is used).
1165 static gboolean do_find(char *path, char *dummy)
1167 FindInfo info;
1168 char *slash;
1170 check_flags();
1172 if (!quiet)
1174 g_string_sprintf(message, "?Check '%s'?", path);
1175 if (!reply(from_parent, FALSE))
1176 return FALSE;
1179 for (;;)
1181 if (new_entry_string)
1183 if (find_condition)
1184 find_condition_free(find_condition);
1185 find_condition = find_compile(new_entry_string);
1186 g_free(new_entry_string);
1187 new_entry_string = NULL;
1190 if (find_condition)
1191 break;
1193 g_string_assign(message,
1194 "!Invalid find condition - change it and try again\n");
1195 send();
1196 g_string_sprintf(message, "?Check '%s'?", path);
1197 if (!reply(from_parent, TRUE))
1198 return FALSE;
1201 if (mc_lstat(path, &info.stats))
1203 send_error();
1204 g_string_sprintf(message, "'(while checking '%s')\n", path);
1205 send();
1206 return FALSE;
1209 info.fullpath = path;
1210 time(&info.now); /* XXX: Not for each check! */
1212 slash = strrchr(path, '/');
1213 info.leaf = slash ? slash + 1 : path;
1214 info.prune = FALSE;
1215 if (find_test_condition(find_condition, &info))
1217 g_string_sprintf(message, "=%s", path);
1218 send();
1221 if (S_ISDIR(info.stats.st_mode) && !info.prune)
1223 char *safe_path;
1224 safe_path = g_strdup(path);
1225 for_dir_contents(do_find, safe_path, safe_path);
1226 g_free(safe_path);
1229 return FALSE;
1232 static gboolean do_chmod(char *path, char *dummy)
1234 struct stat info;
1235 mode_t new_mode;
1237 check_flags();
1239 if (mc_lstat(path, &info))
1241 send_error();
1242 return FALSE;
1244 if (S_ISLNK(info.st_mode))
1245 return FALSE;
1247 if (!quiet)
1249 g_string_sprintf(message, "?Change permissions of '%s'?", path);
1250 if (!reply(from_parent, FALSE))
1251 return FALSE;
1253 else if (!o_brief)
1255 g_string_sprintf(message, "'Changing permissions of '%s'\n",
1256 path);
1257 send();
1260 for (;;)
1262 if (new_entry_string)
1264 if (mode_change)
1265 mode_free(mode_change);
1266 mode_change = mode_compile(new_entry_string,
1267 MODE_MASK_ALL);
1268 g_free(new_entry_string);
1269 new_entry_string = NULL;
1272 if (mode_change)
1273 break;
1275 g_string_assign(message,
1276 "!Invalid mode command - change it and try again\n");
1277 send();
1278 g_string_sprintf(message, "?Change permissions of '%s'?", path);
1279 if (!reply(from_parent, TRUE))
1280 return FALSE;
1283 if (mc_lstat(path, &info))
1285 send_error();
1286 return FALSE;
1288 if (S_ISLNK(info.st_mode))
1289 return FALSE;
1291 new_mode = mode_adjust(info.st_mode, mode_change);
1292 if (chmod(path, new_mode))
1294 send_error();
1295 return FALSE;
1298 if (o_recurse && S_ISDIR(info.st_mode))
1300 char *safe_path;
1301 safe_path = g_strdup(path);
1302 for_dir_contents(do_chmod, safe_path, safe_path);
1303 g_free(safe_path);
1306 return TRUE;
1309 /* We want to copy 'object' into directory 'dir'. If 'action_leaf'
1310 * is set then that is the new leafname, otherwise the leafname stays
1311 * the same.
1313 static char *make_dest_path(char *object, char *dir)
1315 char *leaf;
1317 if (action_leaf)
1318 leaf = action_leaf;
1319 else
1321 leaf = strrchr(object, '/');
1322 if (!leaf)
1323 leaf = object; /* Error? */
1324 else
1325 leaf++;
1328 return make_path(dir, leaf)->str;
1331 /* If action_leaf is not NULL it specifies the new leaf name
1333 static gboolean do_copy2(char *path, char *dest)
1335 char *dest_path;
1336 struct stat info;
1337 struct stat dest_info;
1338 gboolean retval = TRUE;
1340 check_flags();
1342 dest_path = make_dest_path(path, dest);
1344 if (mc_lstat(path, &info))
1346 send_error();
1347 return FALSE;
1350 if (mc_lstat(dest_path, &dest_info) == 0)
1352 int err;
1353 gboolean merge;
1355 merge = S_ISDIR(info.st_mode) && S_ISDIR(dest_info.st_mode);
1357 g_string_sprintf(message, "?'%s' already exists - %s?",
1358 dest_path,
1359 merge ? "merge contents" : "overwrite");
1361 if (!reply(from_parent, TRUE))
1362 return FALSE;
1364 if (!merge)
1366 if (S_ISDIR(dest_info.st_mode))
1367 err = rmdir(dest_path);
1368 else
1369 err = unlink(dest_path);
1371 if (err)
1373 send_error();
1374 if (errno != ENOENT)
1375 return FALSE;
1376 g_string_sprintf(message,
1377 "'Trying copy anyway...\n");
1378 send();
1382 else if (!quiet)
1384 g_string_sprintf(message, "?Copy %s as %s?", path, dest_path);
1385 if (!reply(from_parent, FALSE))
1386 return FALSE;
1388 else
1390 g_string_sprintf(message, "'Copying %s as %s\n", path,
1391 dest_path);
1392 send();
1395 if (S_ISDIR(info.st_mode))
1397 char *safe_path, *safe_dest;
1398 struct stat dest_info;
1399 gboolean exists;
1401 /* (we will do the update ourselves now, rather than
1402 * afterwards)
1404 retval = FALSE;
1406 safe_path = g_strdup(path);
1407 safe_dest = g_strdup(dest_path);
1409 exists = !mc_lstat(dest_path, &dest_info);
1411 if (exists && !S_ISDIR(dest_info.st_mode))
1413 g_string_sprintf(message,
1414 "!ERROR: Destination already exists, "
1415 "but is not a directory\n");
1417 else if (exists == FALSE && mkdir(dest_path, info.st_mode))
1418 send_error();
1419 else
1421 if (!exists)
1423 /* (just been created then) */
1424 g_string_sprintf(message, "+%s", dest);
1425 send();
1428 action_leaf = NULL;
1429 for_dir_contents(do_copy2, safe_path, safe_dest);
1430 /* Note: dest_path now invalid... */
1433 g_free(safe_path);
1434 g_free(safe_dest);
1436 else if (S_ISLNK(info.st_mode))
1438 char target[MAXPATHLEN + 1];
1439 int count;
1441 /* Not all versions of cp(1) can make symlinks,
1442 * so we special-case it.
1445 count = readlink(path, target, sizeof(target) - 1);
1446 if (count < 0)
1448 send_error();
1449 retval = FALSE;
1451 else
1453 target[count] = '\0';
1454 if (symlink(target, dest_path))
1456 send_error();
1457 retval = FALSE;
1461 else
1463 guchar *error;
1465 error = copy_file(path, dest_path);
1467 if (error)
1469 g_string_sprintf(message, "!ERROR: %s\n", error);
1470 send();
1471 retval = FALSE;
1475 return retval;
1478 /* Copy path to dest.
1479 * Check that path not copied into itself.
1481 static gboolean do_copy(char *path, char *dest)
1483 if (is_sub_dir(make_dest_path(path, dest), path))
1485 g_string_sprintf(message,
1486 "!ERROR: Can't copy directory into itself\n");
1487 send();
1488 return FALSE;
1490 return do_copy2(path, dest);
1493 /* Move path to dest.
1494 * Check that path not moved into itself.
1496 static gboolean do_move(char *path, char *dest)
1498 char *dest_path;
1499 char *leaf;
1500 gboolean retval = TRUE;
1501 char *argv[] = {"mv", "-f", NULL, NULL, NULL};
1502 struct stat info2;
1503 gboolean is_dir;
1505 if (is_sub_dir(dest, path))
1507 g_string_sprintf(message,
1508 "!ERROR: Can't move directory into itself\n");
1509 send();
1510 return FALSE;
1513 check_flags();
1515 leaf = strrchr(path, '/');
1516 if (!leaf)
1517 leaf = path; /* Error? */
1518 else
1519 leaf++;
1521 dest_path = make_path(dest, leaf)->str;
1523 is_dir = mc_lstat(path, &info2) == 0 && S_ISDIR(info2.st_mode);
1525 if (access(dest_path, F_OK) == 0)
1527 struct stat info;
1528 int err;
1530 g_string_sprintf(message, "?'%s' already exists - overwrite?",
1531 dest_path);
1532 if (!reply(from_parent, TRUE))
1533 return FALSE;
1535 if (mc_lstat(dest_path, &info))
1537 send_error();
1538 return FALSE;
1541 if (S_ISDIR(info.st_mode))
1542 err = rmdir(dest_path);
1543 else
1544 err = unlink(dest_path);
1546 if (err)
1548 send_error();
1549 if (errno != ENOENT)
1550 return FALSE;
1551 g_string_sprintf(message,
1552 "'Trying move anyway...\n");
1553 send();
1556 else if (!quiet)
1558 g_string_sprintf(message, "?Move %s as %s?", path, dest_path);
1559 if (!reply(from_parent, FALSE))
1560 return FALSE;
1562 else
1564 g_string_sprintf(message, "'Moving %s as %s\n", path,
1565 dest_path);
1566 send();
1569 argv[2] = path;
1570 argv[3] = dest;
1572 if (fork_exec_wait(argv) == 0)
1574 g_string_sprintf(message, "+%s", path);
1575 g_string_truncate(message, leaf - path);
1576 send();
1577 if (is_dir) {
1578 g_string_sprintf(message, "m%s", path);
1579 send();
1582 else
1584 g_string_sprintf(message, "!ERROR: Failed to move %s as %s\n",
1585 path, dest_path);
1586 send();
1587 retval = FALSE;
1590 return retval;
1593 static gboolean do_link(char *path, char *dest)
1595 char *dest_path;
1596 char *leaf;
1598 check_flags();
1600 leaf = strrchr(path, '/');
1601 if (!leaf)
1602 leaf = path; /* Error? */
1603 else
1604 leaf++;
1606 dest_path = make_path(dest, leaf)->str;
1608 if (quiet)
1610 g_string_sprintf(message, "'Linking %s as %s\n", path,
1611 dest_path);
1612 send();
1614 else
1616 g_string_sprintf(message, "?Link %s as %s?", path, dest_path);
1617 if (!reply(from_parent, FALSE))
1618 return FALSE;
1621 if (symlink(path, dest_path))
1623 send_error();
1624 return FALSE;
1627 return TRUE;
1630 /* Mount/umount this item */
1631 static void do_mount(FilerWindow *filer_window, DirItem *item)
1633 char *argv[3] = {NULL, NULL, NULL};
1634 char *action;
1636 check_flags();
1638 argv[0] = item->flags & ITEM_FLAG_MOUNTED ? "umount" : "mount";
1639 action = item->flags & ITEM_FLAG_MOUNTED ? "Unmount" : "Mount";
1640 argv[1] = make_path(filer_window->path, item->leafname)->str;
1642 if (quiet)
1644 g_string_sprintf(message, "'%sing %s\n", action, argv[1]);
1645 send();
1647 else
1649 g_string_sprintf(message, "?%s %s?", action, argv[1]);
1650 if (!reply(from_parent, FALSE))
1651 return;
1654 if (fork_exec_wait(argv) == 0)
1656 g_string_sprintf(message, "+%s", filer_window->path);
1657 send();
1658 g_string_sprintf(message, "m%s", argv[1]);
1659 send();
1661 else
1663 g_string_sprintf(message, "!ERROR: %s failed\n", action);
1664 send();
1668 /* CHILD MAIN LOOPS */
1670 /* After forking, the child calls one of these functions */
1672 static void usage_cb(gpointer data)
1674 FilerWindow *filer_window = (FilerWindow *) data;
1675 Collection *collection = filer_window->collection;
1676 DirItem *item;
1677 int left = collection->number_selected;
1678 int i = -1;
1679 off_t total_size = 0;
1681 send_dir(filer_window->path);
1683 while (left > 0)
1685 i++;
1686 if (!collection->items[i].selected)
1687 continue;
1688 item = (DirItem *) collection->items[i].data;
1689 size_tally = 0;
1690 do_usage(make_path(filer_window->path,
1691 item->leafname)->str,
1692 filer_window->path);
1693 g_string_sprintf(message, "'%s: %s\n",
1694 item->leafname,
1695 format_size((unsigned long) size_tally));
1696 send();
1697 total_size += size_tally;
1698 left--;
1701 g_string_sprintf(message, "'\nTotal: %s\n",
1702 format_size((unsigned long) total_size));
1703 send();
1706 #ifdef DO_MOUNT_POINTS
1707 static void mount_cb(gpointer data)
1709 FilerWindow *filer_window = (FilerWindow *) data;
1710 Collection *collection = filer_window->collection;
1711 DirItem *item;
1712 int i;
1713 gboolean mount_points = FALSE;
1715 send_dir(filer_window->path);
1717 if (mount_item)
1718 do_mount(filer_window, mount_item);
1719 else
1721 for (i = 0; i < collection->number_of_items; i++)
1723 if (!collection->items[i].selected)
1724 continue;
1725 item = (DirItem *) collection->items[i].data;
1726 if (!(item->flags & ITEM_FLAG_MOUNT_POINT))
1727 continue;
1728 mount_points = TRUE;
1730 do_mount(filer_window, item);
1733 if (!mount_points)
1735 g_string_sprintf(message,
1736 "!No mount points selected!\n");
1737 send();
1741 g_string_sprintf(message, "'\nDone\n");
1742 send();
1744 #endif
1746 static void delete_cb(gpointer data)
1748 FilerWindow *filer_window = (FilerWindow *) data;
1749 Collection *collection = filer_window->collection;
1750 DirItem *item;
1751 int left = collection->number_selected;
1752 int i = -1;
1754 send_dir(filer_window->path);
1756 while (left > 0)
1758 i++;
1759 if (!collection->items[i].selected)
1760 continue;
1761 item = (DirItem *) collection->items[i].data;
1762 if (do_delete(make_path(filer_window->path,
1763 item->leafname)->str,
1764 filer_window->path))
1766 g_string_sprintf(message, "+%s", filer_window->path);
1767 send();
1769 left--;
1772 g_string_sprintf(message, "'\nDone\n");
1773 send();
1776 static void find_cb(gpointer data)
1778 FilerWindow *filer_window = (FilerWindow *) data;
1779 Collection *collection = filer_window->collection;
1780 DirItem *item;
1781 int left = collection->number_selected;
1782 int i = -1;
1784 send_dir(filer_window->path);
1786 while (left > 0)
1788 i++;
1789 if (!collection->items[i].selected)
1790 continue;
1791 item = (DirItem *) collection->items[i].data;
1792 do_find(make_path(filer_window->path,
1793 item->leafname)->str,
1794 NULL);
1795 left--;
1798 g_string_sprintf(message, "'\nDone\n");
1799 send();
1802 static void chmod_cb(gpointer data)
1804 FilerWindow *filer_window = (FilerWindow *) data;
1805 Collection *collection = filer_window->collection;
1806 DirItem *item;
1807 int left = collection->number_selected;
1808 int i = -1;
1810 send_dir(filer_window->path);
1812 while (left > 0)
1814 i++;
1815 if (!collection->items[i].selected)
1816 continue;
1817 item = (DirItem *) collection->items[i].data;
1818 if (item->flags & ITEM_FLAG_SYMLINK)
1820 g_string_sprintf(message, "!'%s' is a symbolic link\n",
1821 item->leafname);
1822 send();
1824 else if (do_chmod(make_path(filer_window->path,
1825 item->leafname)->str, NULL))
1827 g_string_sprintf(message, "+%s", filer_window->path);
1828 send();
1830 left--;
1833 g_string_sprintf(message, "'\nDone\n");
1834 send();
1837 static void list_cb(gpointer data)
1839 GSList *paths = (GSList *) data;
1841 while (paths)
1843 send_dir((char *) paths->data);
1845 if (action_do_func((char *) paths->data, action_dest))
1847 g_string_sprintf(message, "+%s", action_dest);
1848 send();
1851 paths = paths->next;
1854 g_string_sprintf(message, "'\nDone\n");
1855 send();
1858 static void add_toggle(GUIside *gui_side, guchar *label, guchar *code)
1860 GtkWidget *check;
1862 check = gtk_check_button_new_with_label(label);
1863 gtk_object_set_data(GTK_OBJECT(check), "send-code", code);
1864 gtk_signal_connect(GTK_OBJECT(check), "clicked",
1865 button_reply, gui_side);
1866 gtk_box_pack_start(GTK_BOX(gui_side->vbox), check, FALSE, TRUE, 0);
1870 /* EXTERNAL INTERFACE */
1872 void action_find(FilerWindow *filer_window)
1874 GUIside *gui_side;
1875 Collection *collection;
1876 GtkWidget *hbox, *label, *button, *scroller;
1877 gchar *titles[] = {"Name", "Directory"};
1879 collection = filer_window->collection;
1881 if (collection->number_selected < 1)
1883 report_error("ROX-Filer", "You need to select some items "
1884 "to search through");
1885 return;
1888 if (!last_find_string)
1889 last_find_string = g_strdup("'core'");
1891 new_entry_string = last_find_string;
1892 gui_side = start_action(filer_window, find_cb, FALSE);
1893 if (!gui_side)
1894 return;
1896 gui_side->show_info = TRUE;
1898 scroller = gtk_scrolled_window_new(NULL, NULL);
1899 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroller),
1900 GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
1901 gtk_box_pack_start(GTK_BOX(gui_side->vbox), scroller, TRUE, TRUE, 4);
1902 gui_side->results = gtk_clist_new_with_titles(
1903 sizeof(titles) / sizeof(*titles), titles);
1904 gtk_clist_column_titles_passive(GTK_CLIST(gui_side->results));
1905 gtk_widget_set_usize(gui_side->results, 100, 100);
1906 gtk_clist_set_column_width(GTK_CLIST(gui_side->results), 0, 100);
1907 gtk_clist_set_selection_mode(GTK_CLIST(gui_side->results),
1908 GTK_SELECTION_SINGLE);
1909 gtk_container_add(GTK_CONTAINER(scroller), gui_side->results);
1910 gtk_box_set_child_packing(GTK_BOX(gui_side->vbox),
1911 gui_side->log_hbox, FALSE, TRUE, 4, GTK_PACK_START);
1912 gtk_signal_connect(GTK_OBJECT(gui_side->results), "select_row",
1913 GTK_SIGNAL_FUNC(select_row_callback), gui_side);
1915 hbox = gtk_hbox_new(FALSE, 0);
1916 label = gtk_label_new("Expression:");
1917 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, TRUE, 4);
1918 gui_side->default_string = &last_find_string;
1919 gui_side->entry = gtk_entry_new();
1920 gtk_widget_set_style(gui_side->entry, fixed_style);
1921 gtk_entry_set_text(GTK_ENTRY(gui_side->entry), last_find_string);
1922 gtk_editable_select_region(GTK_EDITABLE(gui_side->entry), 0, -1);
1923 gtk_widget_set_sensitive(gui_side->entry, FALSE);
1924 gtk_box_pack_start(GTK_BOX(hbox), gui_side->entry, TRUE, TRUE, 4);
1925 gtk_signal_connect(GTK_OBJECT(gui_side->entry), "changed",
1926 entry_changed, gui_side);
1927 gtk_box_pack_start(GTK_BOX(gui_side->vbox), hbox, FALSE, TRUE, 4);
1928 button = gtk_button_new_with_label("Show expression reference");
1929 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, TRUE, 4);
1930 gtk_signal_connect_object(GTK_OBJECT(button), "clicked",
1931 show_condition_help, NULL);
1933 gtk_window_set_title(GTK_WINDOW(gui_side->window), "Find");
1934 gtk_window_set_focus(GTK_WINDOW(gui_side->window), gui_side->entry);
1935 gtk_signal_connect_object(GTK_OBJECT(gui_side->entry), "activate",
1936 gtk_button_clicked, GTK_OBJECT(gui_side->quiet));
1937 number_of_windows++;
1938 gtk_widget_show_all(gui_side->window);
1941 /* Count disk space used by selected items */
1942 void action_usage(FilerWindow *filer_window)
1944 GUIside *gui_side;
1945 Collection *collection;
1947 collection = filer_window->collection;
1949 if (collection->number_selected < 1)
1951 report_error("ROX-Filer", "You need to select some items "
1952 "to count");
1953 return;
1956 gui_side = start_action(filer_window, usage_cb, TRUE);
1957 if (!gui_side)
1958 return;
1960 gui_side->show_info = TRUE;
1962 gtk_window_set_title(GTK_WINDOW(gui_side->window), "Disk Usage");
1963 number_of_windows++;
1964 gtk_widget_show_all(gui_side->window);
1967 /* Mount/unmount 'item', or all selected mount points if NULL. */
1968 void action_mount(FilerWindow *filer_window, DirItem *item)
1970 #ifdef DO_MOUNT_POINTS
1971 GUIside *gui_side;
1972 Collection *collection;
1974 collection = filer_window->collection;
1976 if (item == NULL && collection->number_selected < 1)
1978 report_error("ROX-Filer", "You need to select some items "
1979 "to mount or unmount");
1980 return;
1983 mount_item = item;
1984 gui_side = start_action(filer_window, mount_cb, o_auto_mount);
1985 if (!gui_side)
1986 return;
1988 gtk_window_set_title(GTK_WINDOW(gui_side->window), "Mount / Unmount");
1989 number_of_windows++;
1990 gtk_widget_show_all(gui_side->window);
1991 #else
1992 report_error("ROX-Filer",
1993 "ROX-Filer does not yet support mount points on your "
1994 "system. Sorry.");
1995 #endif /* DO_MOUNT_POINTS */
1998 /* Deletes all selected items in the window */
1999 void action_delete(FilerWindow *filer_window)
2001 GUIside *gui_side;
2002 Collection *collection;
2004 collection = filer_window->collection;
2006 if (collection->number_selected < 1)
2008 report_error("ROX-Filer", "You need to select some items "
2009 "to delete");
2010 return;
2013 gui_side = start_action(filer_window, delete_cb, o_auto_delete);
2014 if (!gui_side)
2015 return;
2017 gtk_window_set_title(GTK_WINDOW(gui_side->window), "Delete");
2018 add_toggle(gui_side,
2019 "Force - don't confirm deletion of non-writeable items", "F");
2020 add_toggle(gui_side,
2021 "Brief - only log directories being deleted", "B");
2023 number_of_windows++;
2024 gtk_widget_show_all(gui_side->window);
2027 /* Change the permissions of the selected items */
2028 void action_chmod(FilerWindow *filer_window)
2030 GUIside *gui_side;
2031 Collection *collection;
2032 GtkWidget *hbox, *label, *button;
2034 collection = filer_window->collection;
2036 if (collection->number_selected < 1)
2038 report_error("ROX-Filer", "You need to select the items "
2039 "whose permissions you want to change");
2040 return;
2043 if (!last_chmod_string)
2044 last_chmod_string = g_strdup("a+x");
2045 new_entry_string = last_chmod_string;
2046 gui_side = start_action(filer_window, chmod_cb, FALSE);
2047 if (!gui_side)
2048 return;
2050 add_toggle(gui_side,
2051 "Brief - don't list processed files", "B");
2052 add_toggle(gui_side,
2053 "Recurse - also change contents of subdirectories", "R");
2054 hbox = gtk_hbox_new(FALSE, 0);
2055 label = gtk_label_new("Command:");
2056 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, TRUE, 4);
2057 gui_side->default_string = &last_chmod_string;
2058 gui_side->entry = gtk_entry_new();
2059 gtk_entry_set_text(GTK_ENTRY(gui_side->entry), last_chmod_string);
2060 gtk_editable_select_region(GTK_EDITABLE(gui_side->entry), 0, -1);
2061 gtk_widget_set_sensitive(gui_side->entry, FALSE);
2062 gtk_box_pack_start(GTK_BOX(hbox), gui_side->entry, TRUE, TRUE, 4);
2063 gtk_signal_connect(GTK_OBJECT(gui_side->entry), "changed",
2064 entry_changed, gui_side);
2065 gtk_box_pack_start(GTK_BOX(gui_side->vbox), hbox, FALSE, TRUE, 0);
2066 button = gtk_button_new_with_label("Show command reference");
2067 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, TRUE, 4);
2068 gtk_signal_connect_object(GTK_OBJECT(button), "clicked",
2069 show_chmod_help, NULL);
2071 gtk_window_set_focus(GTK_WINDOW(gui_side->window), gui_side->entry);
2072 gtk_window_set_title(GTK_WINDOW(gui_side->window), "Permissions");
2073 gtk_signal_connect_object(GTK_OBJECT(gui_side->entry), "activate",
2074 gtk_button_clicked, GTK_OBJECT(gui_side->yes));
2076 number_of_windows++;
2077 gtk_widget_show_all(gui_side->window);
2080 void action_copy(GSList *paths, char *dest, char *leaf)
2082 GUIside *gui_side;
2084 action_dest = dest;
2085 action_leaf = leaf;
2086 action_do_func = do_copy;
2087 gui_side = start_action(paths, list_cb, o_auto_copy);
2088 if (!gui_side)
2089 return;
2091 gtk_window_set_title(GTK_WINDOW(gui_side->window), "Copy");
2092 number_of_windows++;
2093 gtk_widget_show_all(gui_side->window);
2096 void action_move(GSList *paths, char *dest)
2098 GUIside *gui_side;
2100 action_dest = dest;
2101 action_do_func = do_move;
2102 gui_side = start_action(paths, list_cb, o_auto_move);
2103 if (!gui_side)
2104 return;
2106 gtk_window_set_title(GTK_WINDOW(gui_side->window), "Move");
2107 number_of_windows++;
2108 gtk_widget_show_all(gui_side->window);
2111 void action_link(GSList *paths, char *dest)
2113 GUIside *gui_side;
2115 action_dest = dest;
2116 action_do_func = do_link;
2117 gui_side = start_action(paths, list_cb, o_auto_link);
2118 if (!gui_side)
2119 return;
2121 gtk_window_set_title(GTK_WINDOW(gui_side->window), "Link");
2122 number_of_windows++;
2123 gtk_widget_show_all(gui_side->window);
2126 void action_init(void)
2128 options_sections = g_slist_prepend(options_sections, &options);
2129 option_register("action_auto_quiet", action_auto_quiet);