r229: Made 'Yes' (or the entry) the default focus in action windows.
[rox-filer.git] / ROX-Filer / src / action.c
blob928f106aee7b11cf4392ab126fd997987344c1fe
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 "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)");
366 gtk_widget_set_style(text, fixed_style);
367 gtk_misc_set_padding(GTK_MISC(text), 4, 4);
368 gtk_label_set_justify(GTK_LABEL(text), GTK_JUSTIFY_LEFT);
369 gtk_container_add(GTK_CONTAINER(frame), text);
370 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 4);
372 frame = gtk_frame_new("Simple Tests");
373 text = gtk_label_new(
374 "IsReg, IsLink, IsDir, IsChar, IsBlock, IsDev, IsPipe, IsSocket (types)\n"
375 "IsSUID, IsSGID, IsSticky, IsReadable, IsWriteable, IsExecutable (permissions)"
376 "\n"
377 "IsEmpty, IsMine\n"
378 "\n"
379 "A pattern in single quotes is a shell-style wildcard pattern to match. If it\n"
380 "contains a slash then the match is agaist the full path; otherwise it is \n"
381 "agaist the leafname only.");
382 gtk_misc_set_padding(GTK_MISC(text), 4, 4);
383 gtk_label_set_justify(GTK_LABEL(text), GTK_JUSTIFY_LEFT);
384 gtk_container_add(GTK_CONTAINER(frame), text);
385 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 4);
387 frame = gtk_frame_new("Comparisons");
388 text = gtk_label_new(
389 "<, <=, =, !=, >, >=, After, Before (compare two values)\n"
390 "5 bytes, 1Kb, 2Mb, 3Gb (file sizes)\n"
391 "2 secs|mins|hours|days|weeks|years ago|hence (times)\n"
392 "atime, ctime, mtime, now, size, inode, nlinks, uid, gid, blocks (values)");
393 gtk_misc_set_padding(GTK_MISC(text), 4, 4);
394 gtk_label_set_justify(GTK_LABEL(text), GTK_JUSTIFY_LEFT);
395 gtk_container_add(GTK_CONTAINER(frame), text);
396 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 4);
398 hbox = gtk_hbox_new(FALSE, 20);
399 gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 0);
401 text = gtk_label_new(
402 "See the ROX-Filer manual for full details.");
403 gtk_box_pack_start(GTK_BOX(hbox), text, TRUE, TRUE, 0);
404 button = gtk_button_new_with_label("Close");
405 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, TRUE, 0);
406 gtk_signal_connect_object(GTK_OBJECT(button), "clicked",
407 gtk_widget_hide, GTK_OBJECT(help));
409 gtk_signal_connect_object(GTK_OBJECT(help), "delete_event",
410 gtk_widget_hide, GTK_OBJECT(help));
413 if (GTK_WIDGET_VISIBLE(help))
414 gtk_widget_hide(help);
415 gtk_widget_show_all(help);
418 static void show_chmod_help(gpointer data)
420 static GtkWidget *help = NULL;
422 if (!help)
424 GtkWidget *text, *vbox, *button, *hbox, *sep;
426 help = gtk_window_new(GTK_WINDOW_DIALOG);
427 gtk_container_set_border_width(GTK_CONTAINER(help), 10);
428 gtk_window_set_title(GTK_WINDOW(help),
429 "Permissions command reference");
431 vbox = gtk_vbox_new(FALSE, 0);
432 gtk_container_add(GTK_CONTAINER(help), vbox);
434 text = gtk_label_new(
435 "The format of a command is:\n"
436 "CHANGE, CHANGE, ...\n"
437 "Each CHANGE is:\n"
438 "WHO HOW PERMISSIONS\n"
439 "WHO is some combination of u, g and o which determines whether to\n"
440 "change the permissions for the User (owner), Group or Others.\n"
441 "HOW is +, - or = to add, remove or set exactly the permissions.\n"
442 "PERMISSIONS is some combination of the letters 'rwxXstugo'\n\n"
444 "Examples:\n"
445 "u+rw (the file owner gains read and write permission)\n"
446 "g=u (the group permissions are set to be the same as the user's)\n"
447 "o=u-w (others get the same permissions as the owner, but without "
448 "write permission)\n"
449 "a+x (everyone gets execute/access permission - same as 'ugo+x')\n"
450 "a+X (directories become accessable by everyone; files which were\n"
451 "executable by anyone become executable by everyone)\n"
452 "u+rw, go+r (two commands at once!)\n"
453 "u+s (set the SetUID bit - often has no effect on script files)\n"
454 "755 (set the permissions directly)\n"
456 "\nSee the chmod(1) man page for full details.");
457 gtk_label_set_justify(GTK_LABEL(text), GTK_JUSTIFY_LEFT);
458 gtk_box_pack_start(GTK_BOX(vbox), text, TRUE, TRUE, 0);
460 hbox = gtk_hbox_new(FALSE, 20);
461 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0);
463 sep = gtk_hseparator_new();
464 gtk_box_pack_start(GTK_BOX(hbox), sep, TRUE, TRUE, 0);
465 button = gtk_button_new_with_label("Close");
466 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, TRUE, 0);
467 gtk_signal_connect_object(GTK_OBJECT(button), "clicked",
468 gtk_widget_hide, GTK_OBJECT(help));
470 gtk_signal_connect_object(GTK_OBJECT(help), "delete_event",
471 gtk_widget_hide, GTK_OBJECT(help));
474 if (GTK_WIDGET_VISIBLE(help))
475 gtk_widget_hide(help);
476 gtk_widget_show_all(help);
479 /* TRUE iff `sub' is (or would be) an object inside the directory `parent',
480 * (or the two are the same directory)
482 static gboolean is_sub_dir(char *sub, char *parent)
484 int parent_len;
485 guchar *real_sub, *real_parent;
486 gboolean retval;
488 real_sub = pathdup(sub);
489 real_parent = pathdup(parent);
491 parent_len = strlen(real_parent);
492 if (strncmp(real_parent, real_sub, parent_len))
493 retval = FALSE;
494 else
496 /* real_sub is at least as long as real_parent and all
497 * characters upto real_parent's length match.
500 retval = real_sub[parent_len] == '\0' ||
501 real_sub[parent_len] == '/';
504 g_free(real_sub);
505 g_free(real_parent);
507 return retval;
510 static gboolean display_dir(gpointer data)
512 GUIside *gui_side = (GUIside *) data;
514 gtk_label_set_text(GTK_LABEL(gui_side->dir), gui_side->next_dir + 1);
515 g_free(gui_side->next_dir);
516 gui_side->next_dir = NULL;
518 return FALSE;
521 static void add_to_results(GUIside *gui_side, gchar *path)
523 gchar *row[] = {"Leaf", "Dir"};
524 gchar *slash;
525 int len;
527 slash = strrchr(path, '/');
528 g_return_if_fail(slash != NULL);
530 len = slash - path;
531 row[1] = g_strndup(path, MAX(len, 1));
532 row[0] = slash + 1;
534 gtk_clist_append(GTK_CLIST(gui_side->results), row);
536 g_free(row[1]);
539 /* Called when the child sends us a message */
540 static void message_from_child(gpointer data,
541 gint source,
542 GdkInputCondition condition)
544 char buf[5];
545 GUIside *gui_side = (GUIside *) data;
546 GtkWidget *log = gui_side->log;
548 if (read_exact(source, buf, 4))
550 ssize_t message_len;
551 char *buffer;
553 buf[4] = '\0';
554 message_len = strtol(buf, NULL, 16);
555 buffer = g_malloc(message_len + 1);
556 if (message_len > 0 && read_exact(source, buffer, message_len))
558 buffer[message_len] = '\0';
559 if (*buffer == '?')
561 SENSITIVE_YESNO(gui_side, TRUE);
562 gtk_window_set_focus(
563 GTK_WINDOW(gui_side->window),
564 gui_side->entry ? gui_side->entry
565 : gui_side->yes);
567 else if (*buffer == '+')
569 refresh_dirs(buffer + 1);
570 g_free(buffer);
571 return;
573 else if (*buffer == '=')
575 add_to_results(gui_side, buffer + 1);
576 g_free(buffer);
577 return;
579 else if (*buffer == 'm')
581 filer_check_mounted(buffer + 1);
582 g_free(buffer);
583 return;
585 else if (*buffer == '/')
587 if (gui_side->next_dir)
588 g_free(gui_side->next_dir);
589 else
590 gui_side->next_timer =
591 gtk_timeout_add(500,
592 display_dir,
593 gui_side);
594 gui_side->next_dir = buffer;
595 return;
597 else if (*buffer == '!')
598 gui_side->errors++;
600 gtk_text_insert(GTK_TEXT(log),
601 NULL,
602 *buffer == '!' ? &red : NULL,
603 NULL,
604 buffer + 1, message_len - 1);
605 g_free(buffer);
606 return;
608 g_print("Child died in the middle of a message.\n");
611 /* The child is dead */
612 gui_side->child = 0;
614 fclose(gui_side->to_child);
615 gui_side->to_child = NULL;
616 close(gui_side->from_child);
617 gdk_input_remove(gui_side->input_tag);
618 gtk_widget_set_sensitive(gui_side->quiet, FALSE);
620 if (gui_side->errors)
622 GString *report;
623 report = g_string_new(NULL);
624 g_string_sprintf(report, "There %s %d error%s.\n",
625 gui_side->errors == 1 ? "was" : "were",
626 gui_side->errors,
627 gui_side->errors == 1 ? "" : "s");
628 gtk_text_insert(GTK_TEXT(log), NULL, &red, NULL,
629 report->str, report->len);
631 g_string_free(report, TRUE);
633 else if (gui_side->show_info == FALSE)
634 gtk_widget_destroy(gui_side->window);
637 /* Scans src_dir, updating dest_path whenever cb returns TRUE */
638 static void for_dir_contents(ForDirCB *cb, char *src_dir, char *dest_path)
640 DIR *d;
641 struct dirent *ent;
642 GSList *list = NULL, *next;
644 d = opendir(src_dir);
645 if (!d)
647 send_error();
648 return;
651 send_dir(src_dir);
653 while ((ent = readdir(d)))
655 if (ent->d_name[0] == '.' && (ent->d_name[1] == '\0'
656 || (ent->d_name[1] == '.' && ent->d_name[2] == '\0')))
657 continue;
658 list = g_slist_append(list, g_strdup(make_path(src_dir,
659 ent->d_name)->str));
661 closedir(d);
663 if (!list)
664 return;
666 next = list;
668 while (next)
670 if (cb((char *) next->data, dest_path))
672 g_string_sprintf(message, "+%s", dest_path);
673 send();
676 g_free(next->data);
677 next = next->next;
679 g_slist_free(list);
680 return;
683 /* Read this many bytes into the buffer. TRUE on success. */
684 static gboolean read_exact(int source, char *buffer, ssize_t len)
686 while (len > 0)
688 ssize_t got;
689 got = read(source, buffer, len);
690 if (got < 1)
691 return FALSE;
692 len -= got;
693 buffer += got;
695 return TRUE;
698 /* Send 'message' to our parent process. TRUE on success. */
699 static gboolean send()
701 char len_buffer[5];
702 ssize_t len;
704 g_return_val_if_fail(message->len < 0xffff, FALSE);
706 sprintf(len_buffer, "%04x", message->len);
707 fwrite(len_buffer, 1, 4, to_parent);
708 len = fwrite(message->str, 1, message->len, to_parent);
709 fflush(to_parent);
710 return len == message->len;
713 /* Set the directory indicator at the top of the window */
714 static gboolean send_dir(char *dir)
716 g_string_sprintf(message, "/%s", dir);
717 return send();
720 static gboolean send_error()
722 g_string_sprintf(message, "!ERROR: %s\n", g_strerror(errno));
723 return send();
726 static void button_reply(GtkWidget *button, GUIside *gui_side)
728 char *text;
730 if (!gui_side->to_child)
731 return;
733 text = gtk_object_get_data(GTK_OBJECT(button), "send-code");
734 g_return_if_fail(text != NULL);
735 fputc(*text, gui_side->to_child);
736 fflush(gui_side->to_child);
738 if (*text == 'Y' || *text == 'N' || *text == 'Q')
739 SENSITIVE_YESNO(gui_side, FALSE);
742 static void process_flag(char flag)
744 switch (flag)
746 case 'Q':
747 quiet = !quiet;
748 break;
749 case 'F':
750 o_force = !o_force;
751 break;
752 case 'R':
753 o_recurse = !o_recurse;
754 break;
755 case 'B':
756 o_brief = !o_brief;
757 break;
758 default:
759 g_string_sprintf(message,
760 "!ERROR: Bad message '%c'\n", flag);
761 send();
762 break;
766 /* If the parent has sent any flag toggles, read them */
767 static void check_flags(void)
769 fd_set set;
770 int got;
771 char retval;
772 struct timeval tv;
774 FD_ZERO(&set);
776 while (1)
778 FD_SET(from_parent, &set);
779 tv.tv_sec = 0;
780 tv.tv_usec = 0;
781 got = select(from_parent + 1, &set, NULL, NULL, &tv);
783 if (got == -1)
784 g_error("select() failed: %s\n", g_strerror(errno));
785 else if (!got)
786 return;
788 got = read(from_parent, &retval, 1);
789 if (got != 1)
790 g_error("read() error: %s\n", g_strerror(errno));
792 process_flag(retval);
796 static void read_new_entry_text(void)
798 int len;
799 char c;
800 GString *new;
802 new = g_string_new(NULL);
804 for (;;)
806 len = read(from_parent, &c, 1);
807 if (len != 1)
809 fprintf(stderr, "read() error: %s\n",
810 g_strerror(errno));
811 _exit(1); /* Parent died? */
814 if (c == '\n')
815 break;
816 g_string_append_c(new, c);
819 g_free(new_entry_string);
820 new_entry_string = new->str;
821 g_string_free(new, FALSE);
824 /* Read until the user sends a reply. If ignore_quiet is TRUE then
825 * the user MUST click Yes or No, else treat quiet on as Yes.
826 * If the user needs prompting then does send().
828 static gboolean reply(int fd, gboolean ignore_quiet)
830 ssize_t len;
831 char retval;
832 gboolean asked = FALSE;
834 while (ignore_quiet || !quiet)
836 if (!asked)
838 send();
839 asked = TRUE;
842 len = read(fd, &retval, 1);
843 if (len != 1)
845 fprintf(stderr, "read() error: %s\n",
846 g_strerror(errno));
847 _exit(1); /* Parent died? */
850 switch (retval)
852 case 'Q':
853 quiet = !quiet;
854 if (ignore_quiet)
856 g_string_assign(message, "?");
857 send();
859 break;
860 case 'Y':
861 g_string_assign(message, "' Yes\n");
862 send();
863 return TRUE;
864 case 'N':
865 g_string_assign(message, "' No\n");
866 send();
867 return FALSE;
868 case 'E':
869 read_new_entry_text();
870 break;
871 default:
872 process_flag(retval);
873 break;
877 if (asked)
879 g_string_assign(message, "' Quiet\n");
880 send();
882 return TRUE;
885 static void destroy_action_window(GtkWidget *widget, gpointer data)
887 GUIside *gui_side = (GUIside *) data;
889 if (gui_side->child)
891 kill(gui_side->child, SIGTERM);
892 fclose(gui_side->to_child);
893 close(gui_side->from_child);
894 gdk_input_remove(gui_side->input_tag);
897 if (gui_side->next_dir)
899 gtk_timeout_remove(gui_side->next_timer);
900 g_free(gui_side->next_dir);
903 if (gui_side->preview)
905 gtk_signal_disconnect_by_data(
906 GTK_OBJECT(gui_side->preview->window),
907 gui_side);
908 gui_side->preview = NULL;
911 g_free(gui_side);
913 if (--number_of_windows < 1)
914 gtk_main_quit();
917 /* Create two pipes, fork() a child and return a pointer to a GUIside struct
918 * (NULL on failure). The child calls func().
920 * If autoq then automatically selects 'Quiet'.
922 static GUIside *start_action(gpointer data, ActionChild *func, gboolean autoq)
924 int filedes[4]; /* 0 and 2 are for reading */
925 GUIside *gui_side;
926 int child;
927 GtkWidget *vbox, *button, *scrollbar, *actions;
928 struct sigaction act;
930 if (pipe(filedes))
932 report_error("ROX-Filer", g_strerror(errno));
933 return NULL;
936 if (pipe(filedes + 2))
938 close(filedes[0]);
939 close(filedes[1]);
940 report_error("ROX-Filer", g_strerror(errno));
941 return NULL;
944 child = fork();
945 switch (child)
947 case -1:
948 report_error("ROX-Filer", g_strerror(errno));
949 return NULL;
950 case 0:
951 /* We are the child */
953 quiet = autoq;
955 /* Reset the SIGCHLD handler */
956 act.sa_handler = SIG_DFL;
957 sigemptyset(&act.sa_mask);
958 act.sa_flags = 0;
959 sigaction(SIGCHLD, &act, NULL);
961 message = g_string_new(NULL);
962 close(filedes[0]);
963 close(filedes[3]);
964 to_parent = fdopen(filedes[1], "wb");
965 from_parent = filedes[2];
966 func(data);
967 _exit(0);
970 /* We are the parent */
971 close(filedes[1]);
972 close(filedes[2]);
973 gui_side = g_malloc(sizeof(GUIside));
974 gui_side->from_child = filedes[0];
975 gui_side->to_child = fdopen(filedes[3], "wb");
976 gui_side->log = NULL;
977 gui_side->child = child;
978 gui_side->errors = 0;
979 gui_side->show_info = FALSE;
980 gui_side->preview = NULL;
981 gui_side->results = NULL;
982 gui_side->entry = NULL;
983 gui_side->default_string = NULL;
985 gui_side->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
986 gtk_container_set_border_width(GTK_CONTAINER(gui_side->window), 2);
987 gtk_window_set_default_size(GTK_WINDOW(gui_side->window), 450, 200);
988 gtk_signal_connect(GTK_OBJECT(gui_side->window), "destroy",
989 GTK_SIGNAL_FUNC(destroy_action_window), gui_side);
991 gui_side->vbox = vbox = gtk_vbox_new(FALSE, 0);
992 gtk_container_add(GTK_CONTAINER(gui_side->window), vbox);
994 gui_side->dir = gtk_label_new("<dir>");
995 gui_side->next_dir = NULL;
996 gtk_misc_set_alignment(GTK_MISC(gui_side->dir), 0.5, 0.5);
997 gtk_box_pack_start(GTK_BOX(vbox), gui_side->dir, FALSE, TRUE, 0);
999 gui_side->log_hbox = gtk_hbox_new(FALSE, 0);
1000 gtk_box_pack_start(GTK_BOX(vbox), gui_side->log_hbox, TRUE, TRUE, 4);
1002 gui_side->log = gtk_text_new(NULL, NULL);
1003 gtk_widget_set_usize(gui_side->log, 400, 100);
1004 gtk_box_pack_start(GTK_BOX(gui_side->log_hbox),
1005 gui_side->log, TRUE, TRUE, 0);
1006 scrollbar = gtk_vscrollbar_new(GTK_TEXT(gui_side->log)->vadj);
1007 gtk_box_pack_start(GTK_BOX(gui_side->log_hbox),
1008 scrollbar, FALSE, TRUE, 0);
1010 actions = gtk_hbox_new(TRUE, 4);
1011 gtk_box_pack_start(GTK_BOX(vbox), actions, FALSE, TRUE, 0);
1013 gui_side->quiet = button = gtk_toggle_button_new_with_label("Quiet");
1014 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), autoq);
1015 gtk_object_set_data(GTK_OBJECT(button), "send-code", "Q");
1016 gtk_box_pack_start(GTK_BOX(actions), button, TRUE, TRUE, 0);
1017 gtk_signal_connect(GTK_OBJECT(button), "clicked",
1018 button_reply, gui_side);
1019 gui_side->yes = button = gtk_button_new_with_label("Yes");
1020 gtk_object_set_data(GTK_OBJECT(button), "send-code", "Y");
1021 gtk_box_pack_start(GTK_BOX(actions), button, TRUE, TRUE, 0);
1022 gtk_signal_connect(GTK_OBJECT(button), "clicked",
1023 button_reply, gui_side);
1024 gui_side->no = button = gtk_button_new_with_label("No");
1025 gtk_object_set_data(GTK_OBJECT(button), "send-code", "N");
1026 gtk_box_pack_start(GTK_BOX(actions), button, TRUE, TRUE, 0);
1027 gtk_signal_connect(GTK_OBJECT(button), "clicked",
1028 button_reply, gui_side);
1029 SENSITIVE_YESNO(gui_side, FALSE);
1031 button = gtk_button_new_with_label("Abort");
1032 gtk_box_pack_start(GTK_BOX(actions), button, TRUE, TRUE, 0);
1033 gtk_signal_connect_object(GTK_OBJECT(button), "clicked",
1034 gtk_widget_destroy, GTK_OBJECT(gui_side->window));
1036 gui_side->input_tag = gdk_input_add(gui_side->from_child,
1037 GDK_INPUT_READ,
1038 message_from_child,
1039 gui_side);
1041 return gui_side;
1044 /* ACTIONS ON ONE ITEM */
1046 /* These may call themselves recursively, or ask questions, etc.
1047 * TRUE iff the directory containing dest_path needs to be rescanned.
1050 /* dest_path is the dir containing src_path.
1051 * Updates the global size_tally.
1053 static gboolean do_usage(char *src_path, char *dest_path)
1055 struct stat info;
1057 check_flags();
1059 if (lstat(src_path, &info))
1061 g_string_sprintf(message, "'%s:\n", src_path);
1062 send();
1063 send_error();
1064 return FALSE;
1067 if (S_ISREG(info.st_mode) || S_ISLNK(info.st_mode))
1068 size_tally += info.st_size;
1069 else if (S_ISDIR(info.st_mode))
1071 g_string_sprintf(message, "?Count contents of %s?",
1072 src_path);
1073 if (reply(from_parent, FALSE))
1075 char *safe_path;
1076 safe_path = g_strdup(src_path);
1077 for_dir_contents(do_usage, safe_path, safe_path);
1078 g_free(safe_path);
1082 return FALSE;
1085 /* dest_path is the dir containing src_path */
1086 static gboolean do_delete(char *src_path, char *dest_path)
1088 struct stat info;
1089 gboolean write_prot;
1091 check_flags();
1093 if (lstat(src_path, &info))
1095 send_error();
1096 return FALSE;
1099 write_prot = S_ISLNK(info.st_mode) ? FALSE
1100 : access(src_path, W_OK) != 0;
1101 if (write_prot || !quiet)
1103 g_string_sprintf(message, "?Delete %s'%s'?",
1104 write_prot ? "WRITE-PROTECTED " : " ",
1105 src_path);
1106 if (!reply(from_parent, write_prot && !o_force))
1107 return FALSE;
1109 else if (!o_brief)
1111 g_string_sprintf(message, "'Deleting '%s'\n", src_path);
1112 send();
1115 if (S_ISDIR(info.st_mode))
1117 char *safe_path;
1118 safe_path = g_strdup(src_path);
1119 for_dir_contents(do_delete, safe_path, safe_path);
1120 if (rmdir(safe_path))
1122 g_free(safe_path);
1123 send_error();
1124 return FALSE;
1126 g_string_sprintf(message, "'Directory '%s' deleted\n",
1127 safe_path);
1128 send();
1129 g_string_sprintf(message, "m%s", safe_path);
1130 send();
1131 g_free(safe_path);
1133 else if (unlink(src_path))
1135 send_error();
1136 return FALSE;
1139 return TRUE;
1142 /* path is the item to check. If is is a directory then we may recurse
1143 * (unless prune is used).
1145 static gboolean do_find(char *path, char *dummy)
1147 struct stat info;
1149 check_flags();
1151 if (!quiet)
1153 g_string_sprintf(message, "?Check '%s'?", path);
1154 if (!reply(from_parent, FALSE))
1155 return FALSE;
1158 for (;;)
1160 if (new_entry_string)
1162 if (find_condition)
1163 find_condition_free(find_condition);
1164 find_condition = find_compile(new_entry_string);
1165 g_free(new_entry_string);
1166 new_entry_string = NULL;
1169 if (find_condition)
1170 break;
1172 g_string_assign(message,
1173 "!Invalid find condition - change it and try again\n");
1174 send();
1175 g_string_sprintf(message, "?Check '%s'?", path);
1176 if (!reply(from_parent, TRUE))
1177 return FALSE;
1180 if (lstat(path, &info))
1182 send_error();
1183 g_string_sprintf(message, "'(while checking '%s')\n", path);
1184 send();
1185 return FALSE;
1188 if (find_test_condition(find_condition, path))
1190 g_string_sprintf(message, "=%s", path);
1191 send();
1194 if (S_ISDIR(info.st_mode))
1196 char *safe_path;
1197 safe_path = g_strdup(path);
1198 for_dir_contents(do_find, safe_path, safe_path);
1199 g_free(safe_path);
1202 return FALSE;
1205 static gboolean do_chmod(char *path, char *dummy)
1207 struct stat info;
1208 mode_t new_mode;
1210 check_flags();
1212 if (lstat(path, &info))
1214 send_error();
1215 return FALSE;
1217 if (S_ISLNK(info.st_mode))
1218 return FALSE;
1220 if (!quiet)
1222 g_string_sprintf(message, "?Change permissions of '%s'?", path);
1223 if (!reply(from_parent, FALSE))
1224 return FALSE;
1226 else if (!o_brief)
1228 g_string_sprintf(message, "'Changing permissions of '%s'\n",
1229 path);
1230 send();
1233 for (;;)
1235 if (new_entry_string)
1237 if (mode_change)
1238 mode_free(mode_change);
1239 mode_change = mode_compile(new_entry_string,
1240 MODE_MASK_ALL);
1241 g_free(new_entry_string);
1242 new_entry_string = NULL;
1245 if (mode_change)
1246 break;
1248 g_string_assign(message,
1249 "!Invalid mode command - change it and try again\n");
1250 send();
1251 g_string_sprintf(message, "?Change permissions of '%s'?", path);
1252 if (!reply(from_parent, TRUE))
1253 return FALSE;
1256 if (lstat(path, &info))
1258 send_error();
1259 return FALSE;
1261 if (S_ISLNK(info.st_mode))
1262 return FALSE;
1264 new_mode = mode_adjust(info.st_mode, mode_change);
1265 if (chmod(path, new_mode))
1267 send_error();
1268 return FALSE;
1271 if (o_recurse && S_ISDIR(info.st_mode))
1273 char *safe_path;
1274 safe_path = g_strdup(path);
1275 for_dir_contents(do_chmod, safe_path, safe_path);
1276 g_free(safe_path);
1279 return TRUE;
1282 /* We want to copy 'object' into directory 'dir'. If 'action_leaf'
1283 * is set then that is the new leafname, otherwise the leafname stays
1284 * the same.
1286 static char *make_dest_path(char *object, char *dir)
1288 char *leaf;
1290 if (action_leaf)
1291 leaf = action_leaf;
1292 else
1294 leaf = strrchr(object, '/');
1295 if (!leaf)
1296 leaf = object; /* Error? */
1297 else
1298 leaf++;
1301 return make_path(dir, leaf)->str;
1304 /* If action_leaf is not NULL it specifies the new leaf name
1306 static gboolean do_copy2(char *path, char *dest)
1308 char *dest_path;
1309 struct stat info;
1310 struct stat dest_info;
1311 gboolean retval = TRUE;
1313 check_flags();
1315 dest_path = make_dest_path(path, dest);
1317 if (lstat(path, &info))
1319 send_error();
1320 return FALSE;
1323 if (lstat(dest_path, &dest_info) == 0)
1325 int err;
1326 gboolean merge;
1328 merge = S_ISDIR(info.st_mode) && S_ISDIR(dest_info.st_mode);
1330 g_string_sprintf(message, "?'%s' already exists - %s?",
1331 dest_path,
1332 merge ? "merge contents" : "overwrite");
1334 if (!reply(from_parent, TRUE))
1335 return FALSE;
1337 if (!merge)
1339 if (S_ISDIR(dest_info.st_mode))
1340 err = rmdir(dest_path);
1341 else
1342 err = unlink(dest_path);
1344 if (err)
1346 send_error();
1347 if (errno != ENOENT)
1348 return FALSE;
1349 g_string_sprintf(message,
1350 "'Trying copy anyway...\n");
1351 send();
1355 else if (!quiet)
1357 g_string_sprintf(message, "?Copy %s as %s?", path, dest_path);
1358 if (!reply(from_parent, FALSE))
1359 return FALSE;
1361 else
1363 g_string_sprintf(message, "'Copying %s as %s\n", path,
1364 dest_path);
1365 send();
1368 if (S_ISDIR(info.st_mode))
1370 char *safe_path, *safe_dest;
1371 struct stat dest_info;
1372 gboolean exists;
1374 /* (we will do the update ourselves now, rather than
1375 * afterwards)
1377 retval = FALSE;
1379 safe_path = g_strdup(path);
1380 safe_dest = g_strdup(dest_path);
1382 exists = !lstat(dest_path, &dest_info);
1384 if (exists && !S_ISDIR(dest_info.st_mode))
1386 g_string_sprintf(message,
1387 "!ERROR: Destination already exists, "
1388 "but is not a directory\n");
1390 else if (exists == FALSE && mkdir(dest_path, info.st_mode))
1391 send_error();
1392 else
1394 if (!exists)
1396 /* (just been created then) */
1397 g_string_sprintf(message, "+%s", dest);
1398 send();
1401 action_leaf = NULL;
1402 for_dir_contents(do_copy2, safe_path, safe_dest);
1403 /* Note: dest_path now invalid... */
1406 g_free(safe_path);
1407 g_free(safe_dest);
1409 else if (S_ISLNK(info.st_mode))
1411 char target[MAXPATHLEN + 1];
1412 int count;
1414 /* Not all versions of cp(1) can make symlinks,
1415 * so we special-case it.
1418 count = readlink(path, target, sizeof(target) - 1);
1419 if (count < 0)
1421 send_error();
1422 retval = FALSE;
1424 else
1426 target[count] = '\0';
1427 if (symlink(target, dest_path))
1429 send_error();
1430 retval = FALSE;
1434 else
1436 char *argv[] = {"cp", "-pRf", NULL, NULL, NULL};
1438 argv[2] = path;
1439 argv[3] = dest_path;
1441 if (fork_exec_wait(argv))
1443 g_string_sprintf(message, "!ERROR: Copy failed\n");
1444 send();
1445 retval = FALSE;
1449 return retval;
1452 /* Copy path to dest.
1453 * Check that path not copied into itself.
1455 static gboolean do_copy(char *path, char *dest)
1457 if (is_sub_dir(make_dest_path(path, dest), path))
1459 g_string_sprintf(message,
1460 "!ERROR: Can't copy directory into itself\n");
1461 send();
1462 return FALSE;
1464 return do_copy2(path, dest);
1467 /* Move path to dest.
1468 * Check that path not moved into itself.
1470 static gboolean do_move(char *path, char *dest)
1472 char *dest_path;
1473 char *leaf;
1474 gboolean retval = TRUE;
1475 char *argv[] = {"mv", "-f", NULL, NULL, NULL};
1476 struct stat info2;
1477 gboolean is_dir;
1479 if (is_sub_dir(dest, path))
1481 g_string_sprintf(message,
1482 "!ERROR: Can't move directory into itself\n");
1483 send();
1484 return FALSE;
1487 check_flags();
1489 leaf = strrchr(path, '/');
1490 if (!leaf)
1491 leaf = path; /* Error? */
1492 else
1493 leaf++;
1495 dest_path = make_path(dest, leaf)->str;
1497 is_dir = lstat(path, &info2) == 0 && S_ISDIR(info2.st_mode);
1499 if (access(dest_path, F_OK) == 0)
1501 struct stat info;
1502 int err;
1504 g_string_sprintf(message, "?'%s' already exists - overwrite?",
1505 dest_path);
1506 if (!reply(from_parent, TRUE))
1507 return FALSE;
1509 if (lstat(dest_path, &info))
1511 send_error();
1512 return FALSE;
1515 if (S_ISDIR(info.st_mode))
1516 err = rmdir(dest_path);
1517 else
1518 err = unlink(dest_path);
1520 if (err)
1522 send_error();
1523 if (errno != ENOENT)
1524 return FALSE;
1525 g_string_sprintf(message,
1526 "'Trying move anyway...\n");
1527 send();
1530 else if (!quiet)
1532 g_string_sprintf(message, "?Move %s as %s?", path, dest_path);
1533 if (!reply(from_parent, FALSE))
1534 return FALSE;
1536 else
1538 g_string_sprintf(message, "'Moving %s as %s\n", path,
1539 dest_path);
1540 send();
1543 argv[2] = path;
1544 argv[3] = dest;
1546 if (fork_exec_wait(argv) == 0)
1548 g_string_sprintf(message, "+%s", path);
1549 g_string_truncate(message, leaf - path);
1550 send();
1551 if (is_dir) {
1552 g_string_sprintf(message, "m%s", path);
1553 send();
1556 else
1558 g_string_sprintf(message, "!ERROR: Failed to move %s as %s\n",
1559 path, dest_path);
1560 send();
1561 retval = FALSE;
1564 return retval;
1567 static gboolean do_link(char *path, char *dest)
1569 char *dest_path;
1570 char *leaf;
1572 check_flags();
1574 leaf = strrchr(path, '/');
1575 if (!leaf)
1576 leaf = path; /* Error? */
1577 else
1578 leaf++;
1580 dest_path = make_path(dest, leaf)->str;
1582 if (quiet)
1584 g_string_sprintf(message, "'Linking %s as %s\n", path,
1585 dest_path);
1586 send();
1588 else
1590 g_string_sprintf(message, "?Link %s as %s?", path, dest_path);
1591 if (!reply(from_parent, FALSE))
1592 return FALSE;
1595 if (symlink(path, dest_path))
1597 send_error();
1598 return FALSE;
1601 return TRUE;
1604 /* Mount/umount this item */
1605 static void do_mount(FilerWindow *filer_window, DirItem *item)
1607 char *argv[3] = {NULL, NULL, NULL};
1608 char *action;
1610 check_flags();
1612 argv[0] = item->flags & ITEM_FLAG_MOUNTED ? "umount" : "mount";
1613 action = item->flags & ITEM_FLAG_MOUNTED ? "Unmount" : "Mount";
1614 argv[1] = make_path(filer_window->path, item->leafname)->str;
1616 if (quiet)
1618 g_string_sprintf(message, "'%sing %s\n", action, argv[1]);
1619 send();
1621 else
1623 g_string_sprintf(message, "?%s %s?", action, argv[1]);
1624 if (!reply(from_parent, FALSE))
1625 return;
1628 if (fork_exec_wait(argv) == 0)
1630 g_string_sprintf(message, "+%s", filer_window->path);
1631 send();
1632 g_string_sprintf(message, "m%s", argv[1]);
1633 send();
1635 else
1637 g_string_sprintf(message, "!ERROR: %s failed\n", action);
1638 send();
1642 /* CHILD MAIN LOOPS */
1644 /* After forking, the child calls one of these functions */
1646 static void usage_cb(gpointer data)
1648 FilerWindow *filer_window = (FilerWindow *) data;
1649 Collection *collection = filer_window->collection;
1650 DirItem *item;
1651 int left = collection->number_selected;
1652 int i = -1;
1653 off_t total_size = 0;
1655 send_dir(filer_window->path);
1657 while (left > 0)
1659 i++;
1660 if (!collection->items[i].selected)
1661 continue;
1662 item = (DirItem *) collection->items[i].data;
1663 size_tally = 0;
1664 do_usage(make_path(filer_window->path,
1665 item->leafname)->str,
1666 filer_window->path);
1667 g_string_sprintf(message, "'%s: %s\n",
1668 item->leafname,
1669 format_size((unsigned long) size_tally));
1670 send();
1671 total_size += size_tally;
1672 left--;
1675 g_string_sprintf(message, "'\nTotal: %s\n",
1676 format_size((unsigned long) total_size));
1677 send();
1680 #ifdef DO_MOUNT_POINTS
1681 static void mount_cb(gpointer data)
1683 FilerWindow *filer_window = (FilerWindow *) data;
1684 Collection *collection = filer_window->collection;
1685 DirItem *item;
1686 int i;
1687 gboolean mount_points = FALSE;
1689 send_dir(filer_window->path);
1691 if (mount_item)
1692 do_mount(filer_window, mount_item);
1693 else
1695 for (i = 0; i < collection->number_of_items; i++)
1697 if (!collection->items[i].selected)
1698 continue;
1699 item = (DirItem *) collection->items[i].data;
1700 if (!(item->flags & ITEM_FLAG_MOUNT_POINT))
1701 continue;
1702 mount_points = TRUE;
1704 do_mount(filer_window, item);
1707 if (!mount_points)
1709 g_string_sprintf(message,
1710 "!No mount points selected!\n");
1711 send();
1715 g_string_sprintf(message, "'\nDone\n");
1716 send();
1718 #endif
1720 static void delete_cb(gpointer data)
1722 FilerWindow *filer_window = (FilerWindow *) data;
1723 Collection *collection = filer_window->collection;
1724 DirItem *item;
1725 int left = collection->number_selected;
1726 int i = -1;
1728 send_dir(filer_window->path);
1730 while (left > 0)
1732 i++;
1733 if (!collection->items[i].selected)
1734 continue;
1735 item = (DirItem *) collection->items[i].data;
1736 if (do_delete(make_path(filer_window->path,
1737 item->leafname)->str,
1738 filer_window->path))
1740 g_string_sprintf(message, "+%s", filer_window->path);
1741 send();
1743 left--;
1746 g_string_sprintf(message, "'\nDone\n");
1747 send();
1750 static void find_cb(gpointer data)
1752 FilerWindow *filer_window = (FilerWindow *) data;
1753 Collection *collection = filer_window->collection;
1754 DirItem *item;
1755 int left = collection->number_selected;
1756 int i = -1;
1758 send_dir(filer_window->path);
1760 while (left > 0)
1762 i++;
1763 if (!collection->items[i].selected)
1764 continue;
1765 item = (DirItem *) collection->items[i].data;
1766 do_find(make_path(filer_window->path,
1767 item->leafname)->str,
1768 NULL);
1769 left--;
1772 g_string_sprintf(message, "'\nDone\n");
1773 send();
1776 static void chmod_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 if (item->flags & ITEM_FLAG_SYMLINK)
1794 g_string_sprintf(message, "!'%s' is a symbolic link\n",
1795 item->leafname);
1796 send();
1798 else if (do_chmod(make_path(filer_window->path,
1799 item->leafname)->str, NULL))
1801 g_string_sprintf(message, "+%s", filer_window->path);
1802 send();
1804 left--;
1807 g_string_sprintf(message, "'\nDone\n");
1808 send();
1811 static void list_cb(gpointer data)
1813 GSList *paths = (GSList *) data;
1815 while (paths)
1817 send_dir((char *) paths->data);
1819 if (action_do_func((char *) paths->data, action_dest))
1821 g_string_sprintf(message, "+%s", action_dest);
1822 send();
1825 paths = paths->next;
1828 g_string_sprintf(message, "'\nDone\n");
1829 send();
1832 static void add_toggle(GUIside *gui_side, guchar *label, guchar *code)
1834 GtkWidget *check;
1836 check = gtk_check_button_new_with_label(label);
1837 gtk_object_set_data(GTK_OBJECT(check), "send-code", code);
1838 gtk_signal_connect(GTK_OBJECT(check), "clicked",
1839 button_reply, gui_side);
1840 gtk_box_pack_start(GTK_BOX(gui_side->vbox), check, FALSE, TRUE, 0);
1844 /* EXTERNAL INTERFACE */
1846 void action_find(FilerWindow *filer_window)
1848 GUIside *gui_side;
1849 Collection *collection;
1850 GtkWidget *hbox, *label, *button, *scroller;
1851 gchar *titles[] = {"Name", "Directory"};
1853 collection = filer_window->collection;
1855 if (collection->number_selected < 1)
1857 report_error("ROX-Filer", "You need to select some items "
1858 "to search through");
1859 return;
1862 if (!last_find_string)
1863 last_find_string = g_strdup("'core'");
1865 new_entry_string = last_find_string;
1866 gui_side = start_action(filer_window, find_cb, FALSE);
1867 if (!gui_side)
1868 return;
1870 gui_side->show_info = TRUE;
1872 scroller = gtk_scrolled_window_new(NULL, NULL);
1873 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroller),
1874 GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
1875 gtk_box_pack_start(GTK_BOX(gui_side->vbox), scroller, TRUE, TRUE, 4);
1876 gui_side->results = gtk_clist_new_with_titles(
1877 sizeof(titles) / sizeof(*titles), titles);
1878 gtk_widget_set_usize(gui_side->results, 100, 100);
1879 gtk_clist_set_column_width(GTK_CLIST(gui_side->results), 0, 100);
1880 gtk_clist_set_selection_mode(GTK_CLIST(gui_side->results),
1881 GTK_SELECTION_SINGLE);
1882 gtk_container_add(GTK_CONTAINER(scroller), gui_side->results);
1883 gtk_box_set_child_packing(GTK_BOX(gui_side->vbox),
1884 gui_side->log_hbox, FALSE, TRUE, 4, GTK_PACK_START);
1885 gtk_signal_connect(GTK_OBJECT(gui_side->results), "select_row",
1886 GTK_SIGNAL_FUNC(select_row_callback), gui_side);
1888 hbox = gtk_hbox_new(FALSE, 0);
1889 label = gtk_label_new("Expression:");
1890 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, TRUE, 4);
1891 gui_side->default_string = &last_find_string;
1892 gui_side->entry = gtk_entry_new();
1893 gtk_widget_set_style(gui_side->entry, fixed_style);
1894 gtk_entry_set_text(GTK_ENTRY(gui_side->entry), last_find_string);
1895 gtk_editable_select_region(GTK_EDITABLE(gui_side->entry), 0, -1);
1896 gtk_widget_set_sensitive(gui_side->entry, FALSE);
1897 gtk_box_pack_start(GTK_BOX(hbox), gui_side->entry, TRUE, TRUE, 4);
1898 gtk_signal_connect(GTK_OBJECT(gui_side->entry), "changed",
1899 entry_changed, gui_side);
1900 gtk_box_pack_start(GTK_BOX(gui_side->vbox), hbox, FALSE, TRUE, 4);
1901 button = gtk_button_new_with_label("Show expression reference");
1902 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, TRUE, 4);
1903 gtk_signal_connect_object(GTK_OBJECT(button), "clicked",
1904 show_condition_help, NULL);
1906 gtk_window_set_title(GTK_WINDOW(gui_side->window), "Find");
1907 gtk_window_set_focus(GTK_WINDOW(gui_side->window), gui_side->entry);
1908 gtk_signal_connect_object(GTK_OBJECT(gui_side->entry), "activate",
1909 gtk_button_clicked, GTK_OBJECT(gui_side->quiet));
1910 number_of_windows++;
1911 gtk_widget_show_all(gui_side->window);
1914 /* Count disk space used by selected items */
1915 void action_usage(FilerWindow *filer_window)
1917 GUIside *gui_side;
1918 Collection *collection;
1920 collection = filer_window->collection;
1922 if (collection->number_selected < 1)
1924 report_error("ROX-Filer", "You need to select some items "
1925 "to count");
1926 return;
1929 gui_side = start_action(filer_window, usage_cb, TRUE);
1930 if (!gui_side)
1931 return;
1933 gui_side->show_info = TRUE;
1935 gtk_window_set_title(GTK_WINDOW(gui_side->window), "Disk Usage");
1936 number_of_windows++;
1937 gtk_widget_show_all(gui_side->window);
1940 /* Mount/unmount 'item', or all selected mount points if NULL. */
1941 void action_mount(FilerWindow *filer_window, DirItem *item)
1943 #ifdef DO_MOUNT_POINTS
1944 GUIside *gui_side;
1945 Collection *collection;
1947 collection = filer_window->collection;
1949 if (item == NULL && collection->number_selected < 1)
1951 report_error("ROX-Filer", "You need to select some items "
1952 "to mount or unmount");
1953 return;
1956 mount_item = item;
1957 gui_side = start_action(filer_window, mount_cb, o_auto_mount);
1958 if (!gui_side)
1959 return;
1961 gtk_window_set_title(GTK_WINDOW(gui_side->window), "Mount / Unmount");
1962 number_of_windows++;
1963 gtk_widget_show_all(gui_side->window);
1964 #else
1965 report_error("ROX-Filer",
1966 "ROX-Filer does not yet support mount points on your "
1967 "system. Sorry.");
1968 #endif /* DO_MOUNT_POINTS */
1971 /* Deletes all selected items in the window */
1972 void action_delete(FilerWindow *filer_window)
1974 GUIside *gui_side;
1975 Collection *collection;
1977 collection = filer_window->collection;
1979 if (collection->number_selected < 1)
1981 report_error("ROX-Filer", "You need to select some items "
1982 "to delete");
1983 return;
1986 gui_side = start_action(filer_window, delete_cb, o_auto_delete);
1987 if (!gui_side)
1988 return;
1990 gtk_window_set_title(GTK_WINDOW(gui_side->window), "Delete");
1991 add_toggle(gui_side,
1992 "Force - don't confirm deletion of non-writeable items", "F");
1993 add_toggle(gui_side,
1994 "Brief - only log directories being deleted", "B");
1996 number_of_windows++;
1997 gtk_widget_show_all(gui_side->window);
2000 /* Change the permissions of the selected items */
2001 void action_chmod(FilerWindow *filer_window)
2003 GUIside *gui_side;
2004 Collection *collection;
2005 GtkWidget *hbox, *label, *button;
2007 collection = filer_window->collection;
2009 if (collection->number_selected < 1)
2011 report_error("ROX-Filer", "You need to select the items "
2012 "whose permissions you want to change");
2013 return;
2016 if (!last_chmod_string)
2017 last_chmod_string = g_strdup("a+x");
2018 new_entry_string = last_chmod_string;
2019 gui_side = start_action(filer_window, chmod_cb, FALSE);
2020 if (!gui_side)
2021 return;
2023 add_toggle(gui_side,
2024 "Brief - don't list processed files", "B");
2025 add_toggle(gui_side,
2026 "Recurse - also change contents of subdirectories", "R");
2027 hbox = gtk_hbox_new(FALSE, 0);
2028 label = gtk_label_new("Command:");
2029 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, TRUE, 4);
2030 gui_side->default_string = &last_chmod_string;
2031 gui_side->entry = gtk_entry_new();
2032 gtk_entry_set_text(GTK_ENTRY(gui_side->entry), last_chmod_string);
2033 gtk_editable_select_region(GTK_EDITABLE(gui_side->entry), 0, -1);
2034 gtk_widget_set_sensitive(gui_side->entry, FALSE);
2035 gtk_box_pack_start(GTK_BOX(hbox), gui_side->entry, TRUE, TRUE, 4);
2036 gtk_signal_connect(GTK_OBJECT(gui_side->entry), "changed",
2037 entry_changed, gui_side);
2038 gtk_box_pack_start(GTK_BOX(gui_side->vbox), hbox, FALSE, TRUE, 0);
2039 button = gtk_button_new_with_label("Show command reference");
2040 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, TRUE, 4);
2041 gtk_signal_connect_object(GTK_OBJECT(button), "clicked",
2042 show_chmod_help, NULL);
2044 gtk_window_set_focus(GTK_WINDOW(gui_side->window), gui_side->entry);
2045 gtk_window_set_title(GTK_WINDOW(gui_side->window), "Permissions");
2046 gtk_signal_connect_object(GTK_OBJECT(gui_side->entry), "activate",
2047 gtk_button_clicked, GTK_OBJECT(gui_side->yes));
2049 number_of_windows++;
2050 gtk_widget_show_all(gui_side->window);
2053 void action_copy(GSList *paths, char *dest, char *leaf)
2055 GUIside *gui_side;
2057 action_dest = dest;
2058 action_leaf = leaf;
2059 action_do_func = do_copy;
2060 gui_side = start_action(paths, list_cb, o_auto_copy);
2061 if (!gui_side)
2062 return;
2064 gtk_window_set_title(GTK_WINDOW(gui_side->window), "Copy");
2065 number_of_windows++;
2066 gtk_widget_show_all(gui_side->window);
2069 void action_move(GSList *paths, char *dest)
2071 GUIside *gui_side;
2073 action_dest = dest;
2074 action_do_func = do_move;
2075 gui_side = start_action(paths, list_cb, o_auto_move);
2076 if (!gui_side)
2077 return;
2079 gtk_window_set_title(GTK_WINDOW(gui_side->window), "Move");
2080 number_of_windows++;
2081 gtk_widget_show_all(gui_side->window);
2084 void action_link(GSList *paths, char *dest)
2086 GUIside *gui_side;
2088 action_dest = dest;
2089 action_do_func = do_link;
2090 gui_side = start_action(paths, list_cb, o_auto_link);
2091 if (!gui_side)
2092 return;
2094 gtk_window_set_title(GTK_WINDOW(gui_side->window), "Link");
2095 number_of_windows++;
2096 gtk_widget_show_all(gui_side->window);
2099 void action_init(void)
2101 options_sections = g_slist_prepend(options_sections, &options);
2102 option_register("action_auto_quiet", action_auto_quiet);