r339: The menu key bindings are now only saved if they actually changed.
[rox-filer.git] / ROX-Filer / src / action.c
blobb5fe6460f6e0b0cce14195d950ffb9780769459c
1 /*
2 * $Id$
4 * ROX-Filer, filer for the ROX desktop project
5 * Copyright (C) 2000, Thomas Leonard, <tal197@users.sourceforge.net>.
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 <errno.h>
33 #include <signal.h>
34 #include <sys/time.h>
36 #include "global.h"
38 #include "action.h"
39 #include "string.h"
40 #include "support.h"
41 #include "gui_support.h"
42 #include "filer.h"
43 #include "main.h"
44 #include "options.h"
45 #include "modechange.h"
46 #include "find.h"
47 #include "dir.h"
49 /* Options bits */
50 static GtkWidget *create_options();
51 static void update_options();
52 static void set_options();
53 static void save_options();
54 static char *action_auto_quiet(char *data);
56 static OptionsSection options =
58 N_("Action window options"),
59 create_options,
60 update_options,
61 set_options,
62 save_options
65 static gboolean o_auto_copy = TRUE;
66 static gboolean o_auto_move = TRUE;
67 static gboolean o_auto_link = TRUE;
68 static gboolean o_auto_delete = FALSE;
69 static gboolean o_auto_mount = TRUE;
71 static GtkWidget *w_auto_copy = NULL;
72 static GtkWidget *w_auto_move = NULL;
73 static GtkWidget *w_auto_link = NULL;
74 static GtkWidget *w_auto_delete = NULL;
75 static GtkWidget *w_auto_mount = NULL;
77 /* Parent->Child messages are one character each:
79 * Q/Y/N Quiet/Yes/No button clicked
80 * F Force deletion of non-writeable items
83 #define SENSITIVE_YESNO(gui_side, state) \
84 do { \
85 gtk_widget_set_sensitive((gui_side)->yes, state); \
86 gtk_widget_set_sensitive((gui_side)->no, state); \
87 if ((gui_side)->entry) \
88 gtk_widget_set_sensitive((gui_side)->entry, state);\
89 } while (0)
91 typedef struct _GUIside GUIside;
92 typedef void ActionChild(gpointer data);
93 typedef gboolean ForDirCB(char *path, char *dest_path);
95 struct _GUIside
97 int from_child; /* File descriptor */
98 FILE *to_child;
99 int input_tag; /* gdk_input_add() */
100 GtkWidget *vbox, *log, *window, *dir, *log_hbox;
101 GtkWidget *quiet, *yes, *no;
102 int child; /* Process ID */
103 int errors;
104 gboolean show_info; /* For Disk Usage */
106 GtkWidget *entry; /* May be NULL */
107 guchar **default_string; /* Changed when the entry changes */
109 char *next_dir; /* NULL => no timer active */
110 gint next_timer;
112 /* Used by Find */
113 FilerWindow *preview;
114 GtkWidget *results;
117 /* These don't need to be in a structure because we fork() before
118 * using them again.
120 static int from_parent = 0;
121 static FILE *to_parent = NULL;
122 static gboolean quiet = FALSE;
123 static GString *message = NULL;
124 static char *action_dest = NULL;
125 static char *action_leaf = NULL;
126 static gboolean (*action_do_func)(char *source, char *dest);
127 static size_t size_tally; /* For Disk Usage */
129 static struct mode_change *mode_change = NULL; /* For Permissions */
130 static FindCondition *find_condition = NULL; /* For Find */
132 static gboolean o_force = FALSE;
133 static gboolean o_brief = FALSE;
134 static gboolean o_recurse = FALSE;
136 /* Whenever the text in these boxes is changed we store a copy of the new
137 * string to be used as the default next time.
139 static guchar *last_chmod_string = NULL;
140 static guchar *last_find_string = NULL;
142 /* Set to one of the above before forking. This may change over a call to
143 * reply(). It is reset to NULL once the text is parsed.
145 static guchar *new_entry_string = NULL;
147 /* Static prototypes */
148 static gboolean send();
149 static gboolean send_error();
150 static gboolean send_dir(char *dir);
151 static gboolean read_exact(int source, char *buffer, ssize_t len);
152 static void do_mount(guchar *path, gboolean mount);
153 static void add_toggle(GUIside *gui_side, guchar *label, guchar *code);
154 static gboolean reply(int fd, gboolean ignore_quiet);
156 /* OPTIONS */
158 /* Build up some option widgets to go in the options dialog, but don't
159 * fill them in yet.
161 static GtkWidget *create_options()
163 GtkWidget *vbox, *hbox, *label;
165 vbox = gtk_vbox_new(FALSE, 0);
166 gtk_container_set_border_width(GTK_CONTAINER(vbox), 4);
168 label = gtk_label_new(_("Auto-start (Quiet) these actions:"));
169 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, TRUE, 0);
171 hbox = gtk_hbox_new(TRUE, 0);
172 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0);
174 w_auto_copy = gtk_check_button_new_with_label(_("Copy"));
175 gtk_box_pack_start(GTK_BOX(hbox), w_auto_copy, FALSE, TRUE, 0);
176 w_auto_move = gtk_check_button_new_with_label(_("Move"));
177 gtk_box_pack_start(GTK_BOX(hbox), w_auto_move, FALSE, TRUE, 0);
178 w_auto_link = gtk_check_button_new_with_label(_("Link"));
179 gtk_box_pack_start(GTK_BOX(hbox), w_auto_link, FALSE, TRUE, 0);
180 w_auto_delete = gtk_check_button_new_with_label(_("Delete"));
181 gtk_box_pack_start(GTK_BOX(hbox), w_auto_delete, FALSE, TRUE, 0);
182 w_auto_mount = gtk_check_button_new_with_label(_("Mount"));
183 gtk_box_pack_start(GTK_BOX(hbox), w_auto_mount, FALSE, TRUE, 0);
185 return vbox;
188 /* Reflect current state by changing the widgets in the options box */
189 static void update_options()
191 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w_auto_copy),
192 o_auto_copy);
193 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w_auto_move),
194 o_auto_move);
195 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w_auto_link),
196 o_auto_link);
197 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w_auto_delete),
198 o_auto_delete);
199 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w_auto_mount),
200 o_auto_mount);
203 /* Set current values by reading the states of the widgets in the options box */
204 static void set_options()
206 o_auto_copy = gtk_toggle_button_get_active(
207 GTK_TOGGLE_BUTTON(w_auto_copy));
208 o_auto_move = gtk_toggle_button_get_active(
209 GTK_TOGGLE_BUTTON(w_auto_move));
210 o_auto_link = gtk_toggle_button_get_active(
211 GTK_TOGGLE_BUTTON(w_auto_link));
212 o_auto_delete = gtk_toggle_button_get_active(
213 GTK_TOGGLE_BUTTON(w_auto_delete));
214 o_auto_mount = gtk_toggle_button_get_active(
215 GTK_TOGGLE_BUTTON(w_auto_mount));
218 static void save_options()
220 guchar str[] = "cmldt";
222 if (o_auto_copy)
223 str[0] = 'C';
224 if (o_auto_move)
225 str[1] = 'M';
226 if (o_auto_link)
227 str[2] = 'L';
228 if (o_auto_delete)
229 str[3] = 'D';
230 if (o_auto_mount)
231 str[4] = 'T';
233 option_write("action_auto_quiet", str);
236 static char *action_auto_quiet(char *data)
238 while (*data)
240 char c = *data++;
241 gboolean state;
243 state = isupper(c);
245 switch (tolower(c))
247 case 'c':
248 o_auto_copy = state;
249 break;
250 case 'm':
251 o_auto_move = state;
252 break;
253 case 'l':
254 o_auto_link = state;
255 break;
256 case 'd':
257 o_auto_delete = state;
258 break;
259 case 't':
260 o_auto_mount = state;
261 break;
262 default:
263 return _("Unknown flag");
267 return NULL;
270 /* SUPPORT */
272 static void preview_closed(GtkWidget *window, GUIside *gui_side)
274 gui_side->preview = NULL;
277 static void select_row_callback(GtkWidget *widget,
278 gint row, gint column,
279 GdkEventButton *event,
280 GUIside *gui_side)
282 gchar *leaf, *dir;
284 gtk_clist_get_text(GTK_CLIST(gui_side->results), row, 0, &leaf);
285 gtk_clist_get_text(GTK_CLIST(gui_side->results), row, 1, &dir);
287 gtk_clist_unselect_row(GTK_CLIST(gui_side->results), row, column);
289 if (gui_side->preview)
291 if (strcmp(gui_side->preview->path, dir) == 0)
292 display_set_autoselect(gui_side->preview, leaf);
293 else
294 filer_change_to(gui_side->preview, dir, leaf);
295 return;
298 gui_side->preview = filer_opendir(dir);
299 if (gui_side->preview)
301 display_set_autoselect(gui_side->preview, leaf);
302 gtk_signal_connect(GTK_OBJECT(gui_side->preview->window),
303 "destroy",
304 GTK_SIGNAL_FUNC(preview_closed), gui_side);
309 /* This is called whenever the user edits the entry box (if any) - send the
310 * new string.
312 static void entry_changed(GtkEntry *entry, GUIside *gui_side)
314 guchar *text;
316 g_return_if_fail(gui_side->default_string != NULL);
318 text = gtk_entry_get_text(entry);
320 g_free(*(gui_side->default_string));
321 *(gui_side->default_string) = g_strdup(text);
323 if (!gui_side->to_child)
324 return;
326 fputc('E', gui_side->to_child);
327 fputs(text, gui_side->to_child);
328 fputc('\n', gui_side->to_child);
329 fflush(gui_side->to_child);
332 void show_condition_help(gpointer data)
334 static GtkWidget *help = NULL;
336 if (!help)
338 GtkWidget *text, *vbox, *button, *hbox, *frame;
340 help = gtk_window_new(GTK_WINDOW_DIALOG);
341 gtk_container_set_border_width(GTK_CONTAINER(help), 10);
342 gtk_window_set_title(GTK_WINDOW(help),
343 _("Find expression reference"));
345 vbox = gtk_vbox_new(FALSE, 0);
346 gtk_container_add(GTK_CONTAINER(help), vbox);
348 frame = gtk_frame_new(_("Quick Start"));
349 text = gtk_label_new(
350 _("Just put the name of the file you're looking for in single quotes:\n"
351 "'index.html' (to find a file called 'index.html')"));
352 gtk_misc_set_padding(GTK_MISC(text), 4, 4);
353 gtk_label_set_justify(GTK_LABEL(text), GTK_JUSTIFY_LEFT);
354 gtk_container_add(GTK_CONTAINER(frame), text);
355 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 4);
357 frame = gtk_frame_new(_("Examples"));
358 text = gtk_label_new(
359 _("'*.htm', '*.html' (finds HTML files)\n"
360 "IsDir 'lib' (finds directories called 'lib')\n"
361 "IsReg 'core' (finds a regular file called 'core')\n"
362 "! (IsDir, IsReg) (is neither a directory nor a regular file)\n"
363 "mtime after 1 day ago and size > 1Mb (big, and recently modified)\n"
364 "'CVS' prune, isreg (a regular file not in CVS)\n"
365 "IsReg system(grep -q fred \"%\") (contains the word 'fred')"));
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 against the full path; otherwise it is \n"
381 "against 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 frame = gtk_frame_new(_("Specials"));
399 text = gtk_label_new(
400 _("system(command) (true if 'command' returns with a zero exit status; a % \n"
401 "in 'command' is replaced with the path of the current file)\n"
402 "prune (false, and prevents searching the contents of a directory).")
404 gtk_misc_set_padding(GTK_MISC(text), 4, 4);
405 gtk_label_set_justify(GTK_LABEL(text), GTK_JUSTIFY_LEFT);
406 gtk_container_add(GTK_CONTAINER(frame), text);
407 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 4);
409 hbox = gtk_hbox_new(FALSE, 20);
410 gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 0);
412 text = gtk_label_new(
413 _("See the ROX-Filer manual for full details."));
414 gtk_box_pack_start(GTK_BOX(hbox), text, TRUE, TRUE, 0);
415 button = gtk_button_new_with_label(_("Close"));
416 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, TRUE, 0);
417 gtk_signal_connect_object(GTK_OBJECT(button), "clicked",
418 gtk_widget_hide, GTK_OBJECT(help));
420 gtk_signal_connect_object(GTK_OBJECT(help), "delete_event",
421 gtk_widget_hide, GTK_OBJECT(help));
424 if (GTK_WIDGET_VISIBLE(help))
425 gtk_widget_hide(help);
426 gtk_widget_show_all(help);
429 static void show_chmod_help(gpointer data)
431 static GtkWidget *help = NULL;
433 if (!help)
435 GtkWidget *text, *vbox, *button, *hbox, *sep;
437 help = gtk_window_new(GTK_WINDOW_DIALOG);
438 gtk_container_set_border_width(GTK_CONTAINER(help), 10);
439 gtk_window_set_title(GTK_WINDOW(help),
440 _("Permissions command reference"));
442 vbox = gtk_vbox_new(FALSE, 0);
443 gtk_container_add(GTK_CONTAINER(help), vbox);
445 text = gtk_label_new(
446 _("The format of a command is:\n"
447 "CHANGE, CHANGE, ...\n"
448 "Each CHANGE is:\n"
449 "WHO HOW PERMISSIONS\n"
450 "WHO is some combination of u, g and o which determines whether to\n"
451 "change the permissions for the User (owner), Group or Others.\n"
452 "HOW is +, - or = to add, remove or set exactly the permissions.\n"
453 "PERMISSIONS is some combination of the letters 'rwxXstugo'\n\n"
455 "Examples:\n"
456 "u+rw (the file owner gains read and write permission)\n"
457 "g=u (the group permissions are set to be the same as the user's)\n"
458 "o=u-w (others get the same permissions as the owner, but without "
459 "write permission)\n"
460 "a+x (everyone gets execute/access permission - same as 'ugo+x')\n"
461 "a+X (directories become accessable by everyone; files which were\n"
462 "executable by anyone become executable by everyone)\n"
463 "u+rw, go+r (two commands at once!)\n"
464 "u+s (set the SetUID bit - often has no effect on script files)\n"
465 "755 (set the permissions directly)\n"
467 "\nSee the chmod(1) man page for full details."));
468 gtk_label_set_justify(GTK_LABEL(text), GTK_JUSTIFY_LEFT);
469 gtk_box_pack_start(GTK_BOX(vbox), text, TRUE, TRUE, 0);
471 hbox = gtk_hbox_new(FALSE, 20);
472 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0);
474 sep = gtk_hseparator_new();
475 gtk_box_pack_start(GTK_BOX(hbox), sep, TRUE, TRUE, 0);
476 button = gtk_button_new_with_label(_("Close"));
477 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, TRUE, 0);
478 gtk_signal_connect_object(GTK_OBJECT(button), "clicked",
479 gtk_widget_hide, GTK_OBJECT(help));
481 gtk_signal_connect_object(GTK_OBJECT(help), "delete_event",
482 gtk_widget_hide, GTK_OBJECT(help));
485 if (GTK_WIDGET_VISIBLE(help))
486 gtk_widget_hide(help);
487 gtk_widget_show_all(help);
490 /* TRUE iff `sub' is (or would be) an object inside the directory `parent',
491 * (or the two are the same directory)
493 static gboolean is_sub_dir(char *sub, char *parent)
495 int parent_len;
496 guchar *real_sub, *real_parent;
497 gboolean retval;
499 real_sub = pathdup(sub);
500 real_parent = pathdup(parent);
502 parent_len = strlen(real_parent);
503 if (strncmp(real_parent, real_sub, parent_len))
504 retval = FALSE;
505 else
507 /* real_sub is at least as long as real_parent and all
508 * characters upto real_parent's length match.
511 retval = real_sub[parent_len] == '\0' ||
512 real_sub[parent_len] == '/';
515 g_free(real_sub);
516 g_free(real_parent);
518 return retval;
521 static gboolean display_dir(gpointer data)
523 GUIside *gui_side = (GUIside *) data;
525 gtk_label_set_text(GTK_LABEL(gui_side->dir), gui_side->next_dir + 1);
526 g_free(gui_side->next_dir);
527 gui_side->next_dir = NULL;
529 return FALSE;
532 static void add_to_results(GUIside *gui_side, gchar *path)
534 gchar *row[] = {"Leaf", "Dir"};
535 gchar *slash;
536 int len;
538 slash = strrchr(path, '/');
539 g_return_if_fail(slash != NULL);
541 len = slash - path;
542 row[1] = g_strndup(path, MAX(len, 1));
543 row[0] = slash + 1;
545 gtk_clist_append(GTK_CLIST(gui_side->results), row);
547 g_free(row[1]);
550 /* Called when the child sends us a message */
551 static void message_from_child(gpointer data,
552 gint source,
553 GdkInputCondition condition)
555 char buf[5];
556 GUIside *gui_side = (GUIside *) data;
557 GtkWidget *log = gui_side->log;
559 if (read_exact(source, buf, 4))
561 ssize_t message_len;
562 char *buffer;
564 buf[4] = '\0';
565 message_len = strtol(buf, NULL, 16);
566 buffer = g_malloc(message_len + 1);
567 if (message_len > 0 && read_exact(source, buffer, message_len))
569 buffer[message_len] = '\0';
570 if (*buffer == '?')
572 SENSITIVE_YESNO(gui_side, TRUE);
573 gtk_window_set_focus(
574 GTK_WINDOW(gui_side->window),
575 gui_side->entry ? gui_side->entry
576 : gui_side->yes);
578 else if (*buffer == '+')
580 refresh_dirs(buffer + 1);
581 g_free(buffer);
582 return;
584 else if (*buffer == '=')
586 add_to_results(gui_side, buffer + 1);
587 g_free(buffer);
588 return;
590 else if (*buffer == 'm')
592 filer_check_mounted(buffer + 1);
593 g_free(buffer);
594 return;
596 else if (*buffer == '/')
598 if (gui_side->next_dir)
599 g_free(gui_side->next_dir);
600 else
601 gui_side->next_timer =
602 gtk_timeout_add(500,
603 display_dir,
604 gui_side);
605 gui_side->next_dir = buffer;
606 return;
608 else if (*buffer == '!')
609 gui_side->errors++;
611 gtk_text_insert(GTK_TEXT(log),
612 NULL,
613 *buffer == '!' ? &red : NULL,
614 NULL,
615 buffer + 1, message_len - 1);
616 g_free(buffer);
617 return;
619 g_printerr("Child died in the middle of a message.\n");
622 /* The child is dead */
623 gui_side->child = 0;
625 fclose(gui_side->to_child);
626 gui_side->to_child = NULL;
627 close(gui_side->from_child);
628 gdk_input_remove(gui_side->input_tag);
629 gtk_widget_set_sensitive(gui_side->quiet, FALSE);
631 if (gui_side->errors)
633 guchar *report;
635 if (gui_side->errors == 1)
636 report = g_strdup(_("There was one error.\n"));
637 else
638 report = g_strdup_printf(_("There were %d errors.\n"),
639 gui_side->errors);
641 gtk_text_insert(GTK_TEXT(log), NULL, &red, NULL,
642 report, -1);
644 g_free(report);
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 /* Message displayed is "ERROR reading 'path': message" */
661 g_string_sprintf(message, "!%s '%s': %s\n",
662 _("ERROR reading"),
663 src_dir, g_strerror(errno));
664 send();
665 return;
668 send_dir(src_dir);
670 while ((ent = mc_readdir(d)))
672 if (ent->d_name[0] == '.' && (ent->d_name[1] == '\0'
673 || (ent->d_name[1] == '.' && ent->d_name[2] == '\0')))
674 continue;
675 list = g_slist_append(list, g_strdup(make_path(src_dir,
676 ent->d_name)->str));
678 mc_closedir(d);
680 if (!list)
681 return;
683 next = list;
685 while (next)
687 if (cb((char *) next->data, dest_path))
689 g_string_sprintf(message, "+%s", dest_path);
690 send();
693 g_free(next->data);
694 next = next->next;
696 g_slist_free(list);
697 return;
700 /* Read this many bytes into the buffer. TRUE on success. */
701 static gboolean read_exact(int source, char *buffer, ssize_t len)
703 while (len > 0)
705 ssize_t got;
706 got = read(source, buffer, len);
707 if (got < 1)
708 return FALSE;
709 len -= got;
710 buffer += got;
712 return TRUE;
715 /* Send 'message' to our parent process. TRUE on success. */
716 static gboolean send()
718 char len_buffer[5];
719 ssize_t len;
721 g_return_val_if_fail(message->len < 0xffff, FALSE);
723 sprintf(len_buffer, "%04x", message->len);
724 fwrite(len_buffer, 1, 4, to_parent);
725 len = fwrite(message->str, 1, message->len, to_parent);
726 fflush(to_parent);
727 return len == message->len;
730 /* Set the directory indicator at the top of the window */
731 static gboolean send_dir(char *dir)
733 g_string_sprintf(message, "/%s", dir);
734 return send();
737 static gboolean send_error()
739 g_string_sprintf(message, "!%s: %s\n", _("ERROR"), g_strerror(errno));
740 return send();
743 static void button_reply(GtkWidget *button, GUIside *gui_side)
745 char *text;
747 if (!gui_side->to_child)
748 return;
750 text = gtk_object_get_data(GTK_OBJECT(button), "send-code");
751 g_return_if_fail(text != NULL);
752 fputc(*text, gui_side->to_child);
753 fflush(gui_side->to_child);
755 if (*text == 'Y' || *text == 'N' || *text == 'Q')
756 SENSITIVE_YESNO(gui_side, FALSE);
759 static void process_flag(char flag)
761 switch (flag)
763 case 'Q':
764 quiet = !quiet;
765 break;
766 case 'F':
767 o_force = !o_force;
768 break;
769 case 'R':
770 o_recurse = !o_recurse;
771 break;
772 case 'B':
773 o_brief = !o_brief;
774 break;
775 default:
776 g_string_sprintf(message,
777 "!ERROR: Bad message '%c'\n", flag);
778 send();
779 break;
783 /* If the parent has sent any flag toggles, read them */
784 static void check_flags(void)
786 fd_set set;
787 int got;
788 char retval;
789 struct timeval tv;
791 FD_ZERO(&set);
793 while (1)
795 FD_SET(from_parent, &set);
796 tv.tv_sec = 0;
797 tv.tv_usec = 0;
798 got = select(from_parent + 1, &set, NULL, NULL, &tv);
800 if (got == -1)
801 g_error("select() failed: %s\n", g_strerror(errno));
802 else if (!got)
803 return;
805 got = read(from_parent, &retval, 1);
806 if (got != 1)
807 g_error("read() error: %s\n", g_strerror(errno));
809 process_flag(retval);
813 static void read_new_entry_text(void)
815 int len;
816 char c;
817 GString *new;
819 new = g_string_new(NULL);
821 for (;;)
823 len = read(from_parent, &c, 1);
824 if (len != 1)
826 fprintf(stderr, "read() error: %s\n",
827 g_strerror(errno));
828 _exit(1); /* Parent died? */
831 if (c == '\n')
832 break;
833 g_string_append_c(new, c);
836 g_free(new_entry_string);
837 new_entry_string = new->str;
838 g_string_free(new, FALSE);
841 /* Read until the user sends a reply. If ignore_quiet is TRUE then
842 * the user MUST click Yes or No, else treat quiet on as Yes.
843 * If the user needs prompting then does send().
845 static gboolean reply(int fd, gboolean ignore_quiet)
847 ssize_t len;
848 char retval;
849 gboolean asked = FALSE;
851 while (ignore_quiet || !quiet)
853 if (!asked)
855 send();
856 asked = TRUE;
859 len = read(fd, &retval, 1);
860 if (len != 1)
862 fprintf(stderr, "read() error: %s\n",
863 g_strerror(errno));
864 _exit(1); /* Parent died? */
867 switch (retval)
869 case 'Q':
870 quiet = !quiet;
871 if (ignore_quiet)
873 g_string_assign(message, "?");
874 send();
876 break;
877 case 'Y':
878 g_string_sprintf(message, "' %s\n", _("Yes"));
879 send();
880 return TRUE;
881 case 'N':
882 g_string_sprintf(message, "' %s\n", _("No"));
883 send();
884 return FALSE;
885 case 'E':
886 read_new_entry_text();
887 break;
888 default:
889 process_flag(retval);
890 break;
894 if (asked)
896 g_string_sprintf(message, "' %s\n", _("Quiet"));
897 send();
899 return TRUE;
902 static void destroy_action_window(GtkWidget *widget, gpointer data)
904 GUIside *gui_side = (GUIside *) data;
906 if (gui_side->child)
908 kill(gui_side->child, SIGTERM);
909 fclose(gui_side->to_child);
910 close(gui_side->from_child);
911 gdk_input_remove(gui_side->input_tag);
914 if (gui_side->next_dir)
916 gtk_timeout_remove(gui_side->next_timer);
917 g_free(gui_side->next_dir);
920 if (gui_side->preview)
922 gtk_signal_disconnect_by_data(
923 GTK_OBJECT(gui_side->preview->window),
924 gui_side);
925 gui_side->preview = NULL;
928 g_free(gui_side);
930 if (--number_of_windows < 1)
931 gtk_main_quit();
934 /* Create two pipes, fork() a child and return a pointer to a GUIside struct
935 * (NULL on failure). The child calls func().
937 * If autoq then automatically selects 'Quiet'.
939 static GUIside *start_action(gpointer data, ActionChild *func, gboolean autoq)
941 int filedes[4]; /* 0 and 2 are for reading */
942 GUIside *gui_side;
943 int child;
944 GtkWidget *vbox, *button, *scrollbar, *actions;
945 struct sigaction act;
947 if (pipe(filedes))
949 report_error(PROJECT, g_strerror(errno));
950 return NULL;
953 if (pipe(filedes + 2))
955 close(filedes[0]);
956 close(filedes[1]);
957 report_error(PROJECT, g_strerror(errno));
958 return NULL;
961 child = fork();
962 switch (child)
964 case -1:
965 report_error(PROJECT, g_strerror(errno));
966 return NULL;
967 case 0:
968 /* We are the child */
970 dup2(to_error_log, STDOUT_FILENO);
971 close_on_exec(STDOUT_FILENO, FALSE);
972 dup2(to_error_log, STDERR_FILENO);
973 close_on_exec(STDERR_FILENO, FALSE);
975 quiet = autoq;
977 /* Reset the SIGCHLD handler */
978 act.sa_handler = SIG_DFL;
979 sigemptyset(&act.sa_mask);
980 act.sa_flags = 0;
981 sigaction(SIGCHLD, &act, NULL);
983 message = g_string_new(NULL);
984 close(filedes[0]);
985 close(filedes[3]);
986 to_parent = fdopen(filedes[1], "wb");
987 from_parent = filedes[2];
988 func(data);
989 send_dir("");
990 _exit(0);
993 /* We are the parent */
994 close(filedes[1]);
995 close(filedes[2]);
996 gui_side = g_malloc(sizeof(GUIside));
997 gui_side->from_child = filedes[0];
998 gui_side->to_child = fdopen(filedes[3], "wb");
999 gui_side->log = NULL;
1000 gui_side->child = child;
1001 gui_side->errors = 0;
1002 gui_side->show_info = FALSE;
1003 gui_side->preview = NULL;
1004 gui_side->results = NULL;
1005 gui_side->entry = NULL;
1006 gui_side->default_string = NULL;
1008 gui_side->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1009 gtk_container_set_border_width(GTK_CONTAINER(gui_side->window), 2);
1010 gtk_window_set_default_size(GTK_WINDOW(gui_side->window), 450, 200);
1011 gtk_signal_connect(GTK_OBJECT(gui_side->window), "destroy",
1012 GTK_SIGNAL_FUNC(destroy_action_window), gui_side);
1014 gui_side->vbox = vbox = gtk_vbox_new(FALSE, 0);
1015 gtk_container_add(GTK_CONTAINER(gui_side->window), vbox);
1017 gui_side->dir = gtk_label_new(_("<dir>"));
1018 gui_side->next_dir = NULL;
1019 gtk_misc_set_alignment(GTK_MISC(gui_side->dir), 0.5, 0.5);
1020 gtk_box_pack_start(GTK_BOX(vbox), gui_side->dir, FALSE, TRUE, 0);
1022 gui_side->log_hbox = gtk_hbox_new(FALSE, 0);
1023 gtk_box_pack_start(GTK_BOX(vbox), gui_side->log_hbox, TRUE, TRUE, 4);
1025 gui_side->log = gtk_text_new(NULL, NULL);
1026 gtk_widget_set_usize(gui_side->log, 400, 100);
1027 gtk_box_pack_start(GTK_BOX(gui_side->log_hbox),
1028 gui_side->log, TRUE, TRUE, 0);
1029 scrollbar = gtk_vscrollbar_new(GTK_TEXT(gui_side->log)->vadj);
1030 gtk_box_pack_start(GTK_BOX(gui_side->log_hbox),
1031 scrollbar, FALSE, TRUE, 0);
1033 actions = gtk_hbox_new(TRUE, 4);
1034 gtk_box_pack_start(GTK_BOX(vbox), actions, FALSE, TRUE, 0);
1036 gui_side->quiet = button = gtk_toggle_button_new_with_label(_("Quiet"));
1037 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), autoq);
1038 gtk_object_set_data(GTK_OBJECT(button), "send-code", "Q");
1039 gtk_box_pack_start(GTK_BOX(actions), button, TRUE, TRUE, 0);
1040 gtk_signal_connect(GTK_OBJECT(button), "clicked",
1041 button_reply, gui_side);
1042 gui_side->yes = button = gtk_button_new_with_label(_("Yes"));
1043 gtk_object_set_data(GTK_OBJECT(button), "send-code", "Y");
1044 gtk_box_pack_start(GTK_BOX(actions), button, TRUE, TRUE, 0);
1045 gtk_signal_connect(GTK_OBJECT(button), "clicked",
1046 button_reply, gui_side);
1047 gui_side->no = button = gtk_button_new_with_label(_("No"));
1048 gtk_object_set_data(GTK_OBJECT(button), "send-code", "N");
1049 gtk_box_pack_start(GTK_BOX(actions), button, TRUE, TRUE, 0);
1050 gtk_signal_connect(GTK_OBJECT(button), "clicked",
1051 button_reply, gui_side);
1052 SENSITIVE_YESNO(gui_side, FALSE);
1054 button = gtk_button_new_with_label(_("Abort"));
1055 gtk_box_pack_start(GTK_BOX(actions), button, TRUE, TRUE, 0);
1056 gtk_signal_connect_object(GTK_OBJECT(button), "clicked",
1057 gtk_widget_destroy, GTK_OBJECT(gui_side->window));
1059 gui_side->input_tag = gdk_input_add(gui_side->from_child,
1060 GDK_INPUT_READ,
1061 message_from_child,
1062 gui_side);
1064 return gui_side;
1067 /* ACTIONS ON ONE ITEM */
1069 /* These may call themselves recursively, or ask questions, etc.
1070 * TRUE iff the directory containing dest_path needs to be rescanned.
1073 /* dest_path is the dir containing src_path.
1074 * Updates the global size_tally.
1076 static gboolean do_usage(char *src_path, char *dest_path)
1078 struct stat info;
1080 check_flags();
1082 if (mc_lstat(src_path, &info))
1084 g_string_sprintf(message, "'%s:\n", src_path);
1085 send();
1086 send_error();
1087 return FALSE;
1090 if (S_ISREG(info.st_mode) || S_ISLNK(info.st_mode))
1091 size_tally += info.st_size;
1092 else if (S_ISDIR(info.st_mode))
1094 g_string_sprintf(message, _("?Count contents of %s?"),
1095 src_path);
1096 if (reply(from_parent, FALSE))
1098 char *safe_path;
1099 safe_path = g_strdup(src_path);
1100 for_dir_contents(do_usage, safe_path, safe_path);
1101 g_free(safe_path);
1105 return FALSE;
1108 /* dest_path is the dir containing src_path */
1109 static gboolean do_delete(char *src_path, char *dest_path)
1111 struct stat info;
1112 gboolean write_prot;
1114 check_flags();
1116 if (mc_lstat(src_path, &info))
1118 send_error();
1119 return FALSE;
1122 write_prot = S_ISLNK(info.st_mode) ? FALSE
1123 : access(src_path, W_OK) != 0;
1124 if (write_prot || !quiet)
1126 g_string_sprintf(message, _("?Delete %s'%s'?"),
1127 write_prot ? _("WRITE-PROTECTED ") : " ",
1128 src_path);
1129 if (!reply(from_parent, write_prot && !o_force))
1130 return FALSE;
1132 else if (!o_brief)
1134 g_string_sprintf(message, _("'Deleting '%s'\n"), src_path);
1135 send();
1138 if (S_ISDIR(info.st_mode))
1140 char *safe_path;
1141 safe_path = g_strdup(src_path);
1142 for_dir_contents(do_delete, safe_path, safe_path);
1143 if (rmdir(safe_path))
1145 g_free(safe_path);
1146 send_error();
1147 return FALSE;
1149 g_string_sprintf(message, _("'Directory '%s' deleted\n"),
1150 safe_path);
1151 send();
1152 g_string_sprintf(message, "m%s", safe_path);
1153 send();
1154 g_free(safe_path);
1156 else if (unlink(src_path))
1158 send_error();
1159 return FALSE;
1162 return TRUE;
1165 /* path is the item to check. If is is a directory then we may recurse
1166 * (unless prune is used).
1168 static gboolean do_find(char *path, char *dummy)
1170 FindInfo info;
1171 char *slash;
1173 check_flags();
1175 if (!quiet)
1177 g_string_sprintf(message, _("?Check '%s'?"), path);
1178 if (!reply(from_parent, FALSE))
1179 return FALSE;
1182 for (;;)
1184 if (new_entry_string)
1186 if (find_condition)
1187 find_condition_free(find_condition);
1188 find_condition = find_compile(new_entry_string);
1189 g_free(new_entry_string);
1190 new_entry_string = NULL;
1193 if (find_condition)
1194 break;
1196 g_string_assign(message, _("!Invalid find condition - "
1197 "change it and try again\n"));
1198 send();
1199 g_string_sprintf(message, _("?Check '%s'?"), path);
1200 if (!reply(from_parent, TRUE))
1201 return FALSE;
1204 if (mc_lstat(path, &info.stats))
1206 send_error();
1207 g_string_sprintf(message, _("'(while checking '%s')\n"), path);
1208 send();
1209 return FALSE;
1212 info.fullpath = path;
1213 time(&info.now); /* XXX: Not for each check! */
1215 slash = strrchr(path, '/');
1216 info.leaf = slash ? slash + 1 : path;
1217 info.prune = FALSE;
1218 if (find_test_condition(find_condition, &info))
1220 g_string_sprintf(message, "=%s", path);
1221 send();
1224 if (S_ISDIR(info.stats.st_mode) && !info.prune)
1226 char *safe_path;
1227 safe_path = g_strdup(path);
1228 for_dir_contents(do_find, safe_path, safe_path);
1229 g_free(safe_path);
1232 return FALSE;
1235 static gboolean do_chmod(char *path, char *dummy)
1237 struct stat info;
1238 mode_t new_mode;
1240 check_flags();
1242 if (mc_lstat(path, &info))
1244 send_error();
1245 return FALSE;
1247 if (S_ISLNK(info.st_mode))
1248 return FALSE;
1250 if (!quiet)
1252 g_string_sprintf(message,
1253 _("?Change permissions of '%s'?"), path);
1254 if (!reply(from_parent, FALSE))
1255 return FALSE;
1257 else if (!o_brief)
1259 g_string_sprintf(message,
1260 _("'Changing permissions of '%s'\n"),
1261 path);
1262 send();
1265 for (;;)
1267 if (new_entry_string)
1269 if (mode_change)
1270 mode_free(mode_change);
1271 mode_change = mode_compile(new_entry_string,
1272 MODE_MASK_ALL);
1273 g_free(new_entry_string);
1274 new_entry_string = NULL;
1277 if (mode_change)
1278 break;
1280 g_string_assign(message,
1281 _("!Invalid mode command - change it and try again\n"));
1282 send();
1283 g_string_sprintf(message,
1284 _("?Change permissions of '%s'?"), path);
1285 if (!reply(from_parent, TRUE))
1286 return FALSE;
1289 if (mc_lstat(path, &info))
1291 send_error();
1292 return FALSE;
1294 if (S_ISLNK(info.st_mode))
1295 return FALSE;
1297 new_mode = mode_adjust(info.st_mode, mode_change);
1298 if (chmod(path, new_mode))
1300 send_error();
1301 return FALSE;
1304 if (o_recurse && S_ISDIR(info.st_mode))
1306 char *safe_path;
1307 safe_path = g_strdup(path);
1308 for_dir_contents(do_chmod, safe_path, safe_path);
1309 g_free(safe_path);
1312 return TRUE;
1315 /* We want to copy 'object' into directory 'dir'. If 'action_leaf'
1316 * is set then that is the new leafname, otherwise the leafname stays
1317 * the same.
1319 static char *make_dest_path(char *object, char *dir)
1321 char *leaf;
1323 if (action_leaf)
1324 leaf = action_leaf;
1325 else
1327 leaf = strrchr(object, '/');
1328 if (!leaf)
1329 leaf = object; /* Error? */
1330 else
1331 leaf++;
1334 return make_path(dir, leaf)->str;
1337 /* If action_leaf is not NULL it specifies the new leaf name
1339 static gboolean do_copy2(char *path, char *dest)
1341 char *dest_path;
1342 struct stat info;
1343 struct stat dest_info;
1344 gboolean retval = TRUE;
1346 check_flags();
1348 dest_path = make_dest_path(path, dest);
1350 if (mc_lstat(path, &info))
1352 send_error();
1353 return FALSE;
1356 if (mc_lstat(dest_path, &dest_info) == 0)
1358 int err;
1359 gboolean merge;
1361 merge = S_ISDIR(info.st_mode) && S_ISDIR(dest_info.st_mode);
1363 g_string_sprintf(message, _("?'%s' already exists - %s?"),
1364 dest_path,
1365 merge ? _("merge contents") : _("overwrite"));
1367 if (!reply(from_parent, TRUE))
1368 return FALSE;
1370 if (!merge)
1372 if (S_ISDIR(dest_info.st_mode))
1373 err = rmdir(dest_path);
1374 else
1375 err = unlink(dest_path);
1377 if (err)
1379 send_error();
1380 if (errno != ENOENT)
1381 return FALSE;
1382 g_string_sprintf(message,
1383 _("'Trying copy anyway...\n"));
1384 send();
1388 else if (!quiet)
1390 g_string_sprintf(message,
1391 _("?Copy %s as %s?"), path, dest_path);
1392 if (!reply(from_parent, FALSE))
1393 return FALSE;
1395 else
1397 g_string_sprintf(message, _("'Copying %s as %s\n"), path,
1398 dest_path);
1399 send();
1402 if (S_ISDIR(info.st_mode))
1404 char *safe_path, *safe_dest;
1405 struct stat dest_info;
1406 gboolean exists;
1408 /* (we will do the update ourselves now, rather than
1409 * afterwards)
1411 retval = FALSE;
1413 safe_path = g_strdup(path);
1414 safe_dest = g_strdup(dest_path);
1416 exists = !mc_lstat(dest_path, &dest_info);
1418 if (exists && !S_ISDIR(dest_info.st_mode))
1420 g_string_sprintf(message,
1421 _("!ERROR: Destination already exists, "
1422 "but is not a directory\n"));
1424 else if (exists == FALSE && mkdir(dest_path, info.st_mode))
1425 send_error();
1426 else
1428 if (!exists)
1430 /* (just been created then) */
1431 g_string_sprintf(message, "+%s", dest);
1432 send();
1435 action_leaf = NULL;
1436 for_dir_contents(do_copy2, safe_path, safe_dest);
1437 /* Note: dest_path now invalid... */
1440 g_free(safe_path);
1441 g_free(safe_dest);
1443 else if (S_ISLNK(info.st_mode))
1445 char target[MAXPATHLEN + 1];
1446 int count;
1448 /* Not all versions of cp(1) can make symlinks,
1449 * so we special-case it.
1452 count = readlink(path, target, sizeof(target) - 1);
1453 if (count < 0)
1455 send_error();
1456 retval = FALSE;
1458 else
1460 target[count] = '\0';
1461 if (symlink(target, dest_path))
1463 send_error();
1464 retval = FALSE;
1468 else
1470 guchar *error;
1472 error = copy_file(path, dest_path);
1474 if (error)
1476 g_string_sprintf(message, _("!ERROR: %s\n"), error);
1477 send();
1478 retval = FALSE;
1482 return retval;
1485 /* Copy path to dest.
1486 * Check that path not copied into itself.
1488 static gboolean do_copy(char *path, char *dest)
1490 if (is_sub_dir(make_dest_path(path, dest), path))
1492 g_string_sprintf(message,
1493 _("!ERROR: Can't copy object into itself\n"));
1494 send();
1495 return FALSE;
1497 return do_copy2(path, dest);
1500 /* Move path to dest.
1501 * Check that path not moved into itself.
1503 static gboolean do_move(char *path, char *dest)
1505 char *dest_path;
1506 char *leaf;
1507 gboolean retval = TRUE;
1508 char *argv[] = {"mv", "-f", NULL, NULL, NULL};
1509 struct stat info2;
1510 gboolean is_dir;
1512 if (is_sub_dir(dest, path))
1514 g_string_sprintf(message,
1515 _("!ERROR: Can't move object into itself\n"));
1516 send();
1517 return FALSE;
1520 check_flags();
1522 leaf = strrchr(path, '/');
1523 if (!leaf)
1524 leaf = path; /* Error? */
1525 else
1526 leaf++;
1528 dest_path = make_path(dest, leaf)->str;
1530 is_dir = mc_lstat(path, &info2) == 0 && S_ISDIR(info2.st_mode);
1532 if (access(dest_path, F_OK) == 0)
1534 struct stat info;
1535 int err;
1537 g_string_sprintf(message,
1538 _("?'%s' already exists - overwrite?"),
1539 dest_path);
1540 if (!reply(from_parent, TRUE))
1541 return FALSE;
1543 if (mc_lstat(dest_path, &info))
1545 send_error();
1546 return FALSE;
1549 if (S_ISDIR(info.st_mode))
1550 err = rmdir(dest_path);
1551 else
1552 err = unlink(dest_path);
1554 if (err)
1556 send_error();
1557 if (errno != ENOENT)
1558 return FALSE;
1559 g_string_sprintf(message,
1560 _("'Trying move anyway...\n"));
1561 send();
1564 else if (!quiet)
1566 g_string_sprintf(message,
1567 _("?Move %s as %s?"), path, dest_path);
1568 if (!reply(from_parent, FALSE))
1569 return FALSE;
1571 else
1573 g_string_sprintf(message, _("'Moving %s as %s\n"), path,
1574 dest_path);
1575 send();
1578 argv[2] = path;
1579 argv[3] = dest;
1581 if (fork_exec_wait(argv) == 0)
1583 g_string_sprintf(message, "+%s", path);
1584 g_string_truncate(message, leaf - path);
1585 send();
1586 if (is_dir) {
1587 g_string_sprintf(message, "m%s", path);
1588 send();
1591 else
1593 g_string_sprintf(message,
1594 _("!ERROR: Failed to move %s as %s\n"),
1595 path, dest_path);
1596 send();
1597 retval = FALSE;
1600 return retval;
1603 static gboolean do_link(char *path, char *dest)
1605 char *dest_path;
1606 char *leaf;
1608 check_flags();
1610 leaf = strrchr(path, '/');
1611 if (!leaf)
1612 leaf = path; /* Error? */
1613 else
1614 leaf++;
1616 dest_path = make_path(dest, leaf)->str;
1618 if (quiet)
1620 g_string_sprintf(message, _("'Linking %s as %s\n"), path,
1621 dest_path);
1622 send();
1624 else
1626 g_string_sprintf(message,
1627 _("?Link %s as %s?"), path, dest_path);
1628 if (!reply(from_parent, FALSE))
1629 return FALSE;
1632 if (symlink(path, dest_path))
1634 send_error();
1635 return FALSE;
1638 return TRUE;
1641 /* Mount/umount this item (depending on 'mount') */
1642 static void do_mount(guchar *path, gboolean mount)
1644 char *argv[3] = {NULL, NULL, NULL};
1646 check_flags();
1648 argv[0] = mount ? "mount" : "umount";
1649 argv[1] = path;
1651 if (quiet)
1653 g_string_sprintf(message,
1654 mount ? _("'Mounting %s\n")
1655 : _("'Unmounting %s\n"),
1656 path);
1657 send();
1659 else
1661 g_string_sprintf(message,
1662 mount ? _("?Mount %s?\n")
1663 : _("?Unmount %s?\n"),
1664 path);
1665 if (!reply(from_parent, FALSE))
1666 return;
1669 if (fork_exec_wait(argv) == 0)
1671 g_string_sprintf(message, "m%s", path);
1672 send();
1674 else
1676 g_string_sprintf(message, _("!ERROR: Mount failed\n"));
1677 send();
1681 /* CHILD MAIN LOOPS */
1683 /* After forking, the child calls one of these functions */
1685 static void usage_cb(gpointer data)
1687 FilerWindow *filer_window = (FilerWindow *) data;
1688 Collection *collection = filer_window->collection;
1689 DirItem *item;
1690 int left = collection->number_selected;
1691 int i = -1;
1692 off_t total_size = 0;
1694 send_dir(filer_window->path);
1696 while (left > 0)
1698 i++;
1699 if (!collection->items[i].selected)
1700 continue;
1701 item = (DirItem *) collection->items[i].data;
1702 size_tally = 0;
1703 do_usage(make_path(filer_window->path,
1704 item->leafname)->str,
1705 filer_window->path);
1706 g_string_sprintf(message, "'%s: %s\n",
1707 item->leafname,
1708 format_size((unsigned long) size_tally));
1709 send();
1710 total_size += size_tally;
1711 left--;
1714 g_string_sprintf(message, _("'\nTotal: %s\n"),
1715 format_size((unsigned long) total_size));
1716 send();
1719 #ifdef DO_MOUNT_POINTS
1720 static void mount_cb(gpointer data)
1722 GList *paths = (GList *) data;
1723 gboolean mount_points = FALSE;
1725 for (; paths; paths = paths->next)
1727 guchar *path = (guchar *) paths->data;
1729 if (g_hash_table_lookup(mtab_mounts, path))
1730 do_mount(path, FALSE); /* Unmount */
1731 else if (g_hash_table_lookup(fstab_mounts, path))
1732 do_mount(path, TRUE); /* Mount */
1733 else
1734 continue;
1736 mount_points = TRUE;
1739 g_string_sprintf(message,
1740 mount_points ? _("'\nDone\n")
1741 : _("!No mount points selected!\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,
1821 _("!'%s' is a symbolic link\n"),
1822 item->leafname);
1823 send();
1825 else if (do_chmod(make_path(filer_window->path,
1826 item->leafname)->str, NULL))
1828 g_string_sprintf(message, "+%s", filer_window->path);
1829 send();
1831 left--;
1834 g_string_sprintf(message, _("'\nDone\n"));
1835 send();
1838 static void list_cb(gpointer data)
1840 GSList *paths = (GSList *) data;
1842 while (paths)
1844 send_dir((char *) paths->data);
1846 if (action_do_func((char *) paths->data, action_dest))
1848 g_string_sprintf(message, "+%s", action_dest);
1849 send();
1852 paths = paths->next;
1855 g_string_sprintf(message, _("'\nDone\n"));
1856 send();
1859 static void add_toggle(GUIside *gui_side, guchar *label, guchar *code)
1861 GtkWidget *check;
1863 check = gtk_check_button_new_with_label(label);
1864 gtk_object_set_data(GTK_OBJECT(check), "send-code", code);
1865 gtk_signal_connect(GTK_OBJECT(check), "clicked",
1866 button_reply, gui_side);
1867 gtk_box_pack_start(GTK_BOX(gui_side->vbox), check, FALSE, TRUE, 0);
1871 /* EXTERNAL INTERFACE */
1873 void action_find(FilerWindow *filer_window)
1875 GUIside *gui_side;
1876 Collection *collection;
1877 GtkWidget *hbox, *label, *scroller;
1878 gchar *titles[2];
1880 titles[0] = _("Name");
1881 titles[1] = _("Directory");
1883 collection = filer_window->collection;
1885 if (collection->number_selected < 1)
1887 report_error(PROJECT, _("You need to select some items "
1888 "to search through"));
1889 return;
1892 if (!last_find_string)
1893 last_find_string = g_strdup("'core'");
1895 new_entry_string = last_find_string;
1896 gui_side = start_action(filer_window, find_cb, FALSE);
1897 if (!gui_side)
1898 return;
1900 gui_side->show_info = TRUE;
1902 scroller = gtk_scrolled_window_new(NULL, NULL);
1903 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroller),
1904 GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
1905 gtk_box_pack_start(GTK_BOX(gui_side->vbox), scroller, TRUE, TRUE, 4);
1906 gui_side->results = gtk_clist_new_with_titles(
1907 sizeof(titles) / sizeof(*titles), titles);
1908 gtk_clist_column_titles_passive(GTK_CLIST(gui_side->results));
1909 gtk_widget_set_usize(gui_side->results, 100, 100);
1910 gtk_clist_set_column_width(GTK_CLIST(gui_side->results), 0, 100);
1911 gtk_clist_set_selection_mode(GTK_CLIST(gui_side->results),
1912 GTK_SELECTION_SINGLE);
1913 gtk_container_add(GTK_CONTAINER(scroller), gui_side->results);
1914 gtk_box_set_child_packing(GTK_BOX(gui_side->vbox),
1915 gui_side->log_hbox, FALSE, TRUE, 4, GTK_PACK_START);
1916 gtk_signal_connect(GTK_OBJECT(gui_side->results), "select_row",
1917 GTK_SIGNAL_FUNC(select_row_callback), gui_side);
1919 hbox = gtk_hbox_new(FALSE, 0);
1920 label = gtk_label_new(_("Expression:"));
1921 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, TRUE, 4);
1922 gui_side->default_string = &last_find_string;
1923 gui_side->entry = gtk_entry_new();
1924 gtk_widget_set_style(gui_side->entry, fixed_style);
1925 gtk_entry_set_text(GTK_ENTRY(gui_side->entry), last_find_string);
1926 gtk_editable_select_region(GTK_EDITABLE(gui_side->entry), 0, -1);
1927 gtk_widget_set_sensitive(gui_side->entry, FALSE);
1928 gtk_box_pack_start(GTK_BOX(hbox), gui_side->entry, TRUE, TRUE, 4);
1929 gtk_signal_connect(GTK_OBJECT(gui_side->entry), "changed",
1930 entry_changed, gui_side);
1931 gtk_box_pack_start(GTK_BOX(gui_side->vbox), hbox, FALSE, TRUE, 4);
1932 gtk_box_pack_start(GTK_BOX(hbox),
1933 new_help_button(show_condition_help, NULL),
1934 FALSE, TRUE, 4);
1936 gtk_window_set_title(GTK_WINDOW(gui_side->window), _("Find"));
1937 gtk_window_set_focus(GTK_WINDOW(gui_side->window), gui_side->entry);
1938 gtk_signal_connect_object(GTK_OBJECT(gui_side->entry), "activate",
1939 gtk_button_clicked, GTK_OBJECT(gui_side->quiet));
1940 number_of_windows++;
1941 gtk_widget_show_all(gui_side->window);
1944 /* Count disk space used by selected items */
1945 void action_usage(FilerWindow *filer_window)
1947 GUIside *gui_side;
1948 Collection *collection;
1950 collection = filer_window->collection;
1952 if (collection->number_selected < 1)
1954 report_error(PROJECT,
1955 _("You need to select some items to count"));
1956 return;
1959 gui_side = start_action(filer_window, usage_cb, TRUE);
1960 if (!gui_side)
1961 return;
1963 gui_side->show_info = TRUE;
1965 gtk_window_set_title(GTK_WINDOW(gui_side->window), _("Disk Usage"));
1966 number_of_windows++;
1967 gtk_widget_show_all(gui_side->window);
1970 /* Mount/unmount listed items (paths).
1971 * Free the list after this function returns.
1973 void action_mount(GList *paths)
1975 #ifdef DO_MOUNT_POINTS
1976 GUIside *gui_side;
1978 gui_side = start_action(paths, mount_cb, o_auto_mount);
1979 if (!gui_side)
1980 return;
1982 gtk_window_set_title(GTK_WINDOW(gui_side->window),
1983 _("Mount / Unmount"));
1984 number_of_windows++;
1985 gtk_widget_show_all(gui_side->window);
1986 #else
1987 report_error(PROJECT,
1988 _("ROX-Filer does not yet support mount points on your "
1989 "system. Sorry."));
1990 #endif /* DO_MOUNT_POINTS */
1993 /* Deletes all selected items in the window */
1994 void action_delete(FilerWindow *filer_window)
1996 GUIside *gui_side;
1997 Collection *collection;
1999 collection = filer_window->collection;
2001 if (collection->number_selected < 1)
2003 report_error(PROJECT,
2004 _("You need to select some items to delete"));
2005 return;
2008 gui_side = start_action(filer_window, delete_cb, o_auto_delete);
2009 if (!gui_side)
2010 return;
2012 gtk_window_set_title(GTK_WINDOW(gui_side->window), _("Delete"));
2013 add_toggle(gui_side,
2014 _("Force - don't confirm deletion of non-writeable items"),
2015 "F");
2016 add_toggle(gui_side,
2017 _("Brief - only log directories being deleted"),
2018 "B");
2020 number_of_windows++;
2021 gtk_widget_show_all(gui_side->window);
2024 /* Change the permissions of the selected items */
2025 void action_chmod(FilerWindow *filer_window)
2027 GUIside *gui_side;
2028 Collection *collection;
2029 GtkWidget *hbox, *label;
2031 collection = filer_window->collection;
2033 if (collection->number_selected < 1)
2035 report_error(PROJECT,
2036 _("You need to select the items "
2037 "whose permissions you want to change"));
2038 return;
2041 if (!last_chmod_string)
2042 last_chmod_string = g_strdup("a+x");
2043 new_entry_string = last_chmod_string;
2044 gui_side = start_action(filer_window, chmod_cb, FALSE);
2045 if (!gui_side)
2046 return;
2048 add_toggle(gui_side,
2049 _("Brief - don't list processed files"), "B");
2050 add_toggle(gui_side,
2051 _("Recurse - also change contents of subdirectories"), "R");
2052 hbox = gtk_hbox_new(FALSE, 0);
2053 label = gtk_label_new(_("Command:"));
2054 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, TRUE, 4);
2055 gui_side->default_string = &last_chmod_string;
2056 gui_side->entry = gtk_entry_new();
2057 gtk_entry_set_text(GTK_ENTRY(gui_side->entry), last_chmod_string);
2058 gtk_editable_select_region(GTK_EDITABLE(gui_side->entry), 0, -1);
2059 gtk_widget_set_sensitive(gui_side->entry, FALSE);
2060 gtk_box_pack_start(GTK_BOX(hbox), gui_side->entry, TRUE, TRUE, 4);
2061 gtk_signal_connect(GTK_OBJECT(gui_side->entry), "changed",
2062 entry_changed, gui_side);
2063 gtk_box_pack_start(GTK_BOX(gui_side->vbox), hbox, FALSE, TRUE, 0);
2064 gtk_box_pack_start(GTK_BOX(hbox),
2065 new_help_button(show_chmod_help, NULL),
2066 FALSE, TRUE, 4);
2068 gtk_window_set_focus(GTK_WINDOW(gui_side->window), gui_side->entry);
2069 gtk_window_set_title(GTK_WINDOW(gui_side->window), _("Permissions"));
2070 gtk_signal_connect_object(GTK_OBJECT(gui_side->entry), "activate",
2071 gtk_button_clicked, GTK_OBJECT(gui_side->yes));
2073 number_of_windows++;
2074 gtk_widget_show_all(gui_side->window);
2077 void action_copy(GSList *paths, char *dest, char *leaf)
2079 GUIside *gui_side;
2081 action_dest = dest;
2082 action_leaf = leaf;
2083 action_do_func = do_copy;
2084 gui_side = start_action(paths, list_cb, o_auto_copy);
2085 if (!gui_side)
2086 return;
2088 gtk_window_set_title(GTK_WINDOW(gui_side->window), _("Copy"));
2089 number_of_windows++;
2090 gtk_widget_show_all(gui_side->window);
2093 void action_move(GSList *paths, char *dest)
2095 GUIside *gui_side;
2097 action_dest = dest;
2098 action_do_func = do_move;
2099 gui_side = start_action(paths, list_cb, o_auto_move);
2100 if (!gui_side)
2101 return;
2103 gtk_window_set_title(GTK_WINDOW(gui_side->window), _("Move"));
2104 number_of_windows++;
2105 gtk_widget_show_all(gui_side->window);
2108 void action_link(GSList *paths, char *dest)
2110 GUIside *gui_side;
2112 action_dest = dest;
2113 action_do_func = do_link;
2114 gui_side = start_action(paths, list_cb, o_auto_link);
2115 if (!gui_side)
2116 return;
2118 gtk_window_set_title(GTK_WINDOW(gui_side->window), _("Link"));
2119 number_of_windows++;
2120 gtk_widget_show_all(gui_side->window);
2123 void action_init(void)
2125 options_sections = g_slist_prepend(options_sections, &options);
2126 option_register("action_auto_quiet", action_auto_quiet);