r343: Updated the docs.
[rox-filer/ma.git] / ROX-Filer / src / action.c
blob17f7384e0a6c1c678fef246c88404280e0a7d182
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 _("Normally, you can just select a command from the menu (click \n"
447 "on the arrow beside the command box). Sometimes, you need more...\n"
448 "\n"
449 "The format of a command is:\n"
450 "CHANGE, CHANGE, ...\n"
451 "Each CHANGE is:\n"
452 "WHO HOW PERMISSIONS\n"
453 "WHO is some combination of u, g and o which determines whether to\n"
454 "change the permissions for the User (owner), Group or Others.\n"
455 "HOW is +, - or = to add, remove or set exactly the permissions.\n"
456 "PERMISSIONS is some combination of the letters 'rwxXstugo'\n\n"
458 "Bracketed text and spaces are ignored.\n\n"
460 "Examples:\n"
461 "u+rw (the file owner gains read and write permission)\n"
462 "g=u (the group permissions are set to be the same as the user's)\n"
463 "o=u-w (others get the same permissions as the owner, but without "
464 "write permission)\n"
465 "a+x (everyone gets execute/access permission - same as 'ugo+x')\n"
466 "a+X (directories become accessable by everyone; files which were\n"
467 "executable by anyone become executable by everyone)\n"
468 "u+rw, go+r (two commands at once!)\n"
469 "u+s (set the SetUID bit - often has no effect on script files)\n"
470 "755 (set the permissions directly)\n"
472 "\nSee the chmod(1) man page for full details."));
473 gtk_label_set_justify(GTK_LABEL(text), GTK_JUSTIFY_LEFT);
474 gtk_box_pack_start(GTK_BOX(vbox), text, TRUE, TRUE, 0);
476 hbox = gtk_hbox_new(FALSE, 20);
477 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0);
479 sep = gtk_hseparator_new();
480 gtk_box_pack_start(GTK_BOX(hbox), sep, TRUE, TRUE, 0);
481 button = gtk_button_new_with_label(_("Close"));
482 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, TRUE, 0);
483 gtk_signal_connect_object(GTK_OBJECT(button), "clicked",
484 gtk_widget_hide, GTK_OBJECT(help));
486 gtk_signal_connect_object(GTK_OBJECT(help), "delete_event",
487 gtk_widget_hide, GTK_OBJECT(help));
490 if (GTK_WIDGET_VISIBLE(help))
491 gtk_widget_hide(help);
492 gtk_widget_show_all(help);
495 /* TRUE iff `sub' is (or would be) an object inside the directory `parent',
496 * (or the two are the same directory)
498 static gboolean is_sub_dir(char *sub, char *parent)
500 int parent_len;
501 guchar *real_sub, *real_parent;
502 gboolean retval;
504 real_sub = pathdup(sub);
505 real_parent = pathdup(parent);
507 parent_len = strlen(real_parent);
508 if (strncmp(real_parent, real_sub, parent_len))
509 retval = FALSE;
510 else
512 /* real_sub is at least as long as real_parent and all
513 * characters upto real_parent's length match.
516 retval = real_sub[parent_len] == '\0' ||
517 real_sub[parent_len] == '/';
520 g_free(real_sub);
521 g_free(real_parent);
523 return retval;
526 static gboolean display_dir(gpointer data)
528 GUIside *gui_side = (GUIside *) data;
530 gtk_label_set_text(GTK_LABEL(gui_side->dir), gui_side->next_dir + 1);
531 g_free(gui_side->next_dir);
532 gui_side->next_dir = NULL;
534 return FALSE;
537 static void add_to_results(GUIside *gui_side, gchar *path)
539 gchar *row[] = {"Leaf", "Dir"};
540 gchar *slash;
541 int len;
543 slash = strrchr(path, '/');
544 g_return_if_fail(slash != NULL);
546 len = slash - path;
547 row[1] = g_strndup(path, MAX(len, 1));
548 row[0] = slash + 1;
550 gtk_clist_append(GTK_CLIST(gui_side->results), row);
552 g_free(row[1]);
555 /* Called when the child sends us a message */
556 static void message_from_child(gpointer data,
557 gint source,
558 GdkInputCondition condition)
560 char buf[5];
561 GUIside *gui_side = (GUIside *) data;
562 GtkWidget *log = gui_side->log;
564 if (read_exact(source, buf, 4))
566 ssize_t message_len;
567 char *buffer;
569 buf[4] = '\0';
570 message_len = strtol(buf, NULL, 16);
571 buffer = g_malloc(message_len + 1);
572 if (message_len > 0 && read_exact(source, buffer, message_len))
574 buffer[message_len] = '\0';
575 if (*buffer == '?')
577 SENSITIVE_YESNO(gui_side, TRUE);
578 gtk_window_set_focus(
579 GTK_WINDOW(gui_side->window),
580 gui_side->entry ? gui_side->entry
581 : gui_side->yes);
583 else if (*buffer == '+')
585 refresh_dirs(buffer + 1);
586 g_free(buffer);
587 return;
589 else if (*buffer == '=')
591 add_to_results(gui_side, buffer + 1);
592 g_free(buffer);
593 return;
595 else if (*buffer == 'm')
597 filer_check_mounted(buffer + 1);
598 g_free(buffer);
599 return;
601 else if (*buffer == '/')
603 if (gui_side->next_dir)
604 g_free(gui_side->next_dir);
605 else
606 gui_side->next_timer =
607 gtk_timeout_add(500,
608 display_dir,
609 gui_side);
610 gui_side->next_dir = buffer;
611 return;
613 else if (*buffer == '!')
614 gui_side->errors++;
616 gtk_text_insert(GTK_TEXT(log),
617 NULL,
618 *buffer == '!' ? &red : NULL,
619 NULL,
620 buffer + 1, message_len - 1);
621 g_free(buffer);
622 return;
624 g_printerr("Child died in the middle of a message.\n");
627 /* The child is dead */
628 gui_side->child = 0;
630 fclose(gui_side->to_child);
631 gui_side->to_child = NULL;
632 close(gui_side->from_child);
633 gdk_input_remove(gui_side->input_tag);
634 gtk_widget_set_sensitive(gui_side->quiet, FALSE);
636 if (gui_side->errors)
638 guchar *report;
640 if (gui_side->errors == 1)
641 report = g_strdup(_("There was one error.\n"));
642 else
643 report = g_strdup_printf(_("There were %d errors.\n"),
644 gui_side->errors);
646 gtk_text_insert(GTK_TEXT(log), NULL, &red, NULL,
647 report, -1);
649 g_free(report);
651 else if (gui_side->show_info == FALSE)
652 gtk_widget_destroy(gui_side->window);
655 /* Scans src_dir, updating dest_path whenever cb returns TRUE */
656 static void for_dir_contents(ForDirCB *cb, char *src_dir, char *dest_path)
658 DIR *d;
659 struct dirent *ent;
660 GSList *list = NULL, *next;
662 d = mc_opendir(src_dir);
663 if (!d)
665 /* Message displayed is "ERROR reading 'path': message" */
666 g_string_sprintf(message, "!%s '%s': %s\n",
667 _("ERROR reading"),
668 src_dir, g_strerror(errno));
669 send();
670 return;
673 send_dir(src_dir);
675 while ((ent = mc_readdir(d)))
677 if (ent->d_name[0] == '.' && (ent->d_name[1] == '\0'
678 || (ent->d_name[1] == '.' && ent->d_name[2] == '\0')))
679 continue;
680 list = g_slist_append(list, g_strdup(make_path(src_dir,
681 ent->d_name)->str));
683 mc_closedir(d);
685 if (!list)
686 return;
688 next = list;
690 while (next)
692 if (cb((char *) next->data, dest_path))
694 g_string_sprintf(message, "+%s", dest_path);
695 send();
698 g_free(next->data);
699 next = next->next;
701 g_slist_free(list);
702 return;
705 /* Read this many bytes into the buffer. TRUE on success. */
706 static gboolean read_exact(int source, char *buffer, ssize_t len)
708 while (len > 0)
710 ssize_t got;
711 got = read(source, buffer, len);
712 if (got < 1)
713 return FALSE;
714 len -= got;
715 buffer += got;
717 return TRUE;
720 /* Send 'message' to our parent process. TRUE on success. */
721 static gboolean send()
723 char len_buffer[5];
724 ssize_t len;
726 g_return_val_if_fail(message->len < 0xffff, FALSE);
728 sprintf(len_buffer, "%04x", message->len);
729 fwrite(len_buffer, 1, 4, to_parent);
730 len = fwrite(message->str, 1, message->len, to_parent);
731 fflush(to_parent);
732 return len == message->len;
735 /* Set the directory indicator at the top of the window */
736 static gboolean send_dir(char *dir)
738 g_string_sprintf(message, "/%s", dir);
739 return send();
742 static gboolean send_error()
744 g_string_sprintf(message, "!%s: %s\n", _("ERROR"), g_strerror(errno));
745 return send();
748 static void button_reply(GtkWidget *button, GUIside *gui_side)
750 char *text;
752 if (!gui_side->to_child)
753 return;
755 text = gtk_object_get_data(GTK_OBJECT(button), "send-code");
756 g_return_if_fail(text != NULL);
757 fputc(*text, gui_side->to_child);
758 fflush(gui_side->to_child);
760 if (*text == 'Y' || *text == 'N' || *text == 'Q')
761 SENSITIVE_YESNO(gui_side, FALSE);
764 static void process_flag(char flag)
766 switch (flag)
768 case 'Q':
769 quiet = !quiet;
770 break;
771 case 'F':
772 o_force = !o_force;
773 break;
774 case 'R':
775 o_recurse = !o_recurse;
776 break;
777 case 'B':
778 o_brief = !o_brief;
779 break;
780 default:
781 g_string_sprintf(message,
782 "!ERROR: Bad message '%c'\n", flag);
783 send();
784 break;
788 /* If the parent has sent any flag toggles, read them */
789 static void check_flags(void)
791 fd_set set;
792 int got;
793 char retval;
794 struct timeval tv;
796 FD_ZERO(&set);
798 while (1)
800 FD_SET(from_parent, &set);
801 tv.tv_sec = 0;
802 tv.tv_usec = 0;
803 got = select(from_parent + 1, &set, NULL, NULL, &tv);
805 if (got == -1)
806 g_error("select() failed: %s\n", g_strerror(errno));
807 else if (!got)
808 return;
810 got = read(from_parent, &retval, 1);
811 if (got != 1)
812 g_error("read() error: %s\n", g_strerror(errno));
814 process_flag(retval);
818 static void read_new_entry_text(void)
820 int len;
821 char c;
822 GString *new;
824 new = g_string_new(NULL);
826 for (;;)
828 len = read(from_parent, &c, 1);
829 if (len != 1)
831 fprintf(stderr, "read() error: %s\n",
832 g_strerror(errno));
833 _exit(1); /* Parent died? */
836 if (c == '\n')
837 break;
838 g_string_append_c(new, c);
841 g_free(new_entry_string);
842 new_entry_string = new->str;
843 g_string_free(new, FALSE);
846 /* Read until the user sends a reply. If ignore_quiet is TRUE then
847 * the user MUST click Yes or No, else treat quiet on as Yes.
848 * If the user needs prompting then does send().
850 static gboolean reply(int fd, gboolean ignore_quiet)
852 ssize_t len;
853 char retval;
854 gboolean asked = FALSE;
856 while (ignore_quiet || !quiet)
858 if (!asked)
860 send();
861 asked = TRUE;
864 len = read(fd, &retval, 1);
865 if (len != 1)
867 fprintf(stderr, "read() error: %s\n",
868 g_strerror(errno));
869 _exit(1); /* Parent died? */
872 switch (retval)
874 case 'Q':
875 quiet = !quiet;
876 if (ignore_quiet)
878 g_string_assign(message, "?");
879 send();
881 break;
882 case 'Y':
883 g_string_sprintf(message, "' %s\n", _("Yes"));
884 send();
885 return TRUE;
886 case 'N':
887 g_string_sprintf(message, "' %s\n", _("No"));
888 send();
889 return FALSE;
890 case 'E':
891 read_new_entry_text();
892 break;
893 default:
894 process_flag(retval);
895 break;
899 if (asked)
901 g_string_sprintf(message, "' %s\n", _("Quiet"));
902 send();
904 return TRUE;
907 static void destroy_action_window(GtkWidget *widget, gpointer data)
909 GUIside *gui_side = (GUIside *) data;
911 if (gui_side->child)
913 kill(gui_side->child, SIGTERM);
914 fclose(gui_side->to_child);
915 close(gui_side->from_child);
916 gdk_input_remove(gui_side->input_tag);
919 if (gui_side->next_dir)
921 gtk_timeout_remove(gui_side->next_timer);
922 g_free(gui_side->next_dir);
925 if (gui_side->preview)
927 gtk_signal_disconnect_by_data(
928 GTK_OBJECT(gui_side->preview->window),
929 gui_side);
930 gui_side->preview = NULL;
933 g_free(gui_side);
935 if (--number_of_windows < 1)
936 gtk_main_quit();
939 /* Create two pipes, fork() a child and return a pointer to a GUIside struct
940 * (NULL on failure). The child calls func().
942 * If autoq then automatically selects 'Quiet'.
944 static GUIside *start_action(gpointer data, ActionChild *func, gboolean autoq)
946 int filedes[4]; /* 0 and 2 are for reading */
947 GUIside *gui_side;
948 int child;
949 GtkWidget *vbox, *button, *scrollbar, *actions;
950 struct sigaction act;
952 if (pipe(filedes))
954 report_error(PROJECT, g_strerror(errno));
955 return NULL;
958 if (pipe(filedes + 2))
960 close(filedes[0]);
961 close(filedes[1]);
962 report_error(PROJECT, g_strerror(errno));
963 return NULL;
966 child = fork();
967 switch (child)
969 case -1:
970 report_error(PROJECT, g_strerror(errno));
971 return NULL;
972 case 0:
973 /* We are the child */
975 dup2(to_error_log, STDOUT_FILENO);
976 close_on_exec(STDOUT_FILENO, FALSE);
977 dup2(to_error_log, STDERR_FILENO);
978 close_on_exec(STDERR_FILENO, FALSE);
980 quiet = autoq;
982 /* Reset the SIGCHLD handler */
983 act.sa_handler = SIG_DFL;
984 sigemptyset(&act.sa_mask);
985 act.sa_flags = 0;
986 sigaction(SIGCHLD, &act, NULL);
988 message = g_string_new(NULL);
989 close(filedes[0]);
990 close(filedes[3]);
991 to_parent = fdopen(filedes[1], "wb");
992 from_parent = filedes[2];
993 func(data);
994 send_dir("");
995 _exit(0);
998 /* We are the parent */
999 close(filedes[1]);
1000 close(filedes[2]);
1001 gui_side = g_malloc(sizeof(GUIside));
1002 gui_side->from_child = filedes[0];
1003 gui_side->to_child = fdopen(filedes[3], "wb");
1004 gui_side->log = NULL;
1005 gui_side->child = child;
1006 gui_side->errors = 0;
1007 gui_side->show_info = FALSE;
1008 gui_side->preview = NULL;
1009 gui_side->results = NULL;
1010 gui_side->entry = NULL;
1011 gui_side->default_string = NULL;
1013 gui_side->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1014 gtk_container_set_border_width(GTK_CONTAINER(gui_side->window), 2);
1015 gtk_window_set_default_size(GTK_WINDOW(gui_side->window), 450, 200);
1016 gtk_signal_connect(GTK_OBJECT(gui_side->window), "destroy",
1017 GTK_SIGNAL_FUNC(destroy_action_window), gui_side);
1019 gui_side->vbox = vbox = gtk_vbox_new(FALSE, 0);
1020 gtk_container_add(GTK_CONTAINER(gui_side->window), vbox);
1022 gui_side->dir = gtk_label_new(_("<dir>"));
1023 gui_side->next_dir = NULL;
1024 gtk_misc_set_alignment(GTK_MISC(gui_side->dir), 0.5, 0.5);
1025 gtk_box_pack_start(GTK_BOX(vbox), gui_side->dir, FALSE, TRUE, 0);
1027 gui_side->log_hbox = gtk_hbox_new(FALSE, 0);
1028 gtk_box_pack_start(GTK_BOX(vbox), gui_side->log_hbox, TRUE, TRUE, 4);
1030 gui_side->log = gtk_text_new(NULL, NULL);
1031 gtk_widget_set_usize(gui_side->log, 400, 100);
1032 gtk_box_pack_start(GTK_BOX(gui_side->log_hbox),
1033 gui_side->log, TRUE, TRUE, 0);
1034 scrollbar = gtk_vscrollbar_new(GTK_TEXT(gui_side->log)->vadj);
1035 gtk_box_pack_start(GTK_BOX(gui_side->log_hbox),
1036 scrollbar, FALSE, TRUE, 0);
1038 actions = gtk_hbox_new(TRUE, 4);
1039 gtk_box_pack_start(GTK_BOX(vbox), actions, FALSE, TRUE, 0);
1041 gui_side->quiet = button = gtk_toggle_button_new_with_label(_("Quiet"));
1042 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), autoq);
1043 gtk_object_set_data(GTK_OBJECT(button), "send-code", "Q");
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->yes = button = gtk_button_new_with_label(_("Yes"));
1048 gtk_object_set_data(GTK_OBJECT(button), "send-code", "Y");
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 gui_side->no = button = gtk_button_new_with_label(_("No"));
1053 gtk_object_set_data(GTK_OBJECT(button), "send-code", "N");
1054 gtk_box_pack_start(GTK_BOX(actions), button, TRUE, TRUE, 0);
1055 gtk_signal_connect(GTK_OBJECT(button), "clicked",
1056 button_reply, gui_side);
1057 SENSITIVE_YESNO(gui_side, FALSE);
1059 button = gtk_button_new_with_label(_("Abort"));
1060 gtk_box_pack_start(GTK_BOX(actions), button, TRUE, TRUE, 0);
1061 gtk_signal_connect_object(GTK_OBJECT(button), "clicked",
1062 gtk_widget_destroy, GTK_OBJECT(gui_side->window));
1064 gui_side->input_tag = gdk_input_add(gui_side->from_child,
1065 GDK_INPUT_READ,
1066 message_from_child,
1067 gui_side);
1069 return gui_side;
1072 /* ACTIONS ON ONE ITEM */
1074 /* These may call themselves recursively, or ask questions, etc.
1075 * TRUE iff the directory containing dest_path needs to be rescanned.
1078 /* dest_path is the dir containing src_path.
1079 * Updates the global size_tally.
1081 static gboolean do_usage(char *src_path, char *dest_path)
1083 struct stat info;
1085 check_flags();
1087 if (mc_lstat(src_path, &info))
1089 g_string_sprintf(message, "'%s:\n", src_path);
1090 send();
1091 send_error();
1092 return FALSE;
1095 if (S_ISREG(info.st_mode) || S_ISLNK(info.st_mode))
1096 size_tally += info.st_size;
1097 else if (S_ISDIR(info.st_mode))
1099 g_string_sprintf(message, _("?Count contents of %s?"),
1100 src_path);
1101 if (reply(from_parent, FALSE))
1103 char *safe_path;
1104 safe_path = g_strdup(src_path);
1105 for_dir_contents(do_usage, safe_path, safe_path);
1106 g_free(safe_path);
1110 return FALSE;
1113 /* dest_path is the dir containing src_path */
1114 static gboolean do_delete(char *src_path, char *dest_path)
1116 struct stat info;
1117 gboolean write_prot;
1119 check_flags();
1121 if (mc_lstat(src_path, &info))
1123 send_error();
1124 return FALSE;
1127 write_prot = S_ISLNK(info.st_mode) ? FALSE
1128 : access(src_path, W_OK) != 0;
1129 if (write_prot || !quiet)
1131 g_string_sprintf(message, _("?Delete %s'%s'?"),
1132 write_prot ? _("WRITE-PROTECTED ") : " ",
1133 src_path);
1134 if (!reply(from_parent, write_prot && !o_force))
1135 return FALSE;
1137 else if (!o_brief)
1139 g_string_sprintf(message, _("'Deleting '%s'\n"), src_path);
1140 send();
1143 if (S_ISDIR(info.st_mode))
1145 char *safe_path;
1146 safe_path = g_strdup(src_path);
1147 for_dir_contents(do_delete, safe_path, safe_path);
1148 if (rmdir(safe_path))
1150 g_free(safe_path);
1151 send_error();
1152 return FALSE;
1154 g_string_sprintf(message, _("'Directory '%s' deleted\n"),
1155 safe_path);
1156 send();
1157 g_string_sprintf(message, "m%s", safe_path);
1158 send();
1159 g_free(safe_path);
1161 else if (unlink(src_path))
1163 send_error();
1164 return FALSE;
1167 return TRUE;
1170 /* path is the item to check. If is is a directory then we may recurse
1171 * (unless prune is used).
1173 static gboolean do_find(char *path, char *dummy)
1175 FindInfo info;
1176 char *slash;
1178 check_flags();
1180 if (!quiet)
1182 g_string_sprintf(message, _("?Check '%s'?"), path);
1183 if (!reply(from_parent, FALSE))
1184 return FALSE;
1187 for (;;)
1189 if (new_entry_string)
1191 if (find_condition)
1192 find_condition_free(find_condition);
1193 find_condition = find_compile(new_entry_string);
1194 g_free(new_entry_string);
1195 new_entry_string = NULL;
1198 if (find_condition)
1199 break;
1201 g_string_assign(message, _("!Invalid find condition - "
1202 "change it and try again\n"));
1203 send();
1204 g_string_sprintf(message, _("?Check '%s'?"), path);
1205 if (!reply(from_parent, TRUE))
1206 return FALSE;
1209 if (mc_lstat(path, &info.stats))
1211 send_error();
1212 g_string_sprintf(message, _("'(while checking '%s')\n"), path);
1213 send();
1214 return FALSE;
1217 info.fullpath = path;
1218 time(&info.now); /* XXX: Not for each check! */
1220 slash = strrchr(path, '/');
1221 info.leaf = slash ? slash + 1 : path;
1222 info.prune = FALSE;
1223 if (find_test_condition(find_condition, &info))
1225 g_string_sprintf(message, "=%s", path);
1226 send();
1229 if (S_ISDIR(info.stats.st_mode) && !info.prune)
1231 char *safe_path;
1232 safe_path = g_strdup(path);
1233 for_dir_contents(do_find, safe_path, safe_path);
1234 g_free(safe_path);
1237 return FALSE;
1240 /* Like mode_compile(), but ignores spaces and bracketed bits */
1241 struct mode_change *nice_mode_compile(const char *mode_string,
1242 unsigned int masked_ops)
1244 GString *new;
1245 int brackets = 0;
1246 struct mode_change *retval = NULL;
1248 new = g_string_new(NULL);
1250 for (; *mode_string; mode_string++)
1252 if (*mode_string == '(')
1253 brackets++;
1254 if (*mode_string == ')')
1256 brackets--;
1257 if (brackets < 0)
1258 break;
1259 continue;
1262 if (brackets == 0 && *mode_string != ' ')
1263 g_string_append_c(new, *mode_string);
1266 if (brackets == 0)
1267 retval = mode_compile(new->str, masked_ops);
1268 g_string_free(new, TRUE);
1269 return retval;
1272 static gboolean do_chmod(char *path, char *dummy)
1274 struct stat info;
1275 mode_t new_mode;
1277 check_flags();
1279 if (mc_lstat(path, &info))
1281 send_error();
1282 return FALSE;
1284 if (S_ISLNK(info.st_mode))
1285 return FALSE;
1287 if (!quiet)
1289 g_string_sprintf(message,
1290 _("?Change permissions of '%s'?"), path);
1291 if (!reply(from_parent, FALSE))
1292 return FALSE;
1294 else if (!o_brief)
1296 g_string_sprintf(message,
1297 _("'Changing permissions of '%s'\n"),
1298 path);
1299 send();
1302 for (;;)
1304 if (new_entry_string)
1306 if (mode_change)
1307 mode_free(mode_change);
1308 mode_change = nice_mode_compile(new_entry_string,
1309 MODE_MASK_ALL);
1310 g_free(new_entry_string);
1311 new_entry_string = NULL;
1314 if (mode_change)
1315 break;
1317 g_string_assign(message,
1318 _("!Invalid mode command - change it and try again\n"));
1319 send();
1320 g_string_sprintf(message,
1321 _("?Change permissions of '%s'?"), path);
1322 if (!reply(from_parent, TRUE))
1323 return FALSE;
1326 if (mc_lstat(path, &info))
1328 send_error();
1329 return FALSE;
1331 if (S_ISLNK(info.st_mode))
1332 return FALSE;
1334 new_mode = mode_adjust(info.st_mode, mode_change);
1335 if (chmod(path, new_mode))
1337 send_error();
1338 return FALSE;
1341 if (o_recurse && S_ISDIR(info.st_mode))
1343 char *safe_path;
1344 safe_path = g_strdup(path);
1345 for_dir_contents(do_chmod, safe_path, safe_path);
1346 g_free(safe_path);
1349 return TRUE;
1352 /* We want to copy 'object' into directory 'dir'. If 'action_leaf'
1353 * is set then that is the new leafname, otherwise the leafname stays
1354 * the same.
1356 static char *make_dest_path(char *object, char *dir)
1358 char *leaf;
1360 if (action_leaf)
1361 leaf = action_leaf;
1362 else
1364 leaf = strrchr(object, '/');
1365 if (!leaf)
1366 leaf = object; /* Error? */
1367 else
1368 leaf++;
1371 return make_path(dir, leaf)->str;
1374 /* If action_leaf is not NULL it specifies the new leaf name */
1375 static gboolean do_copy2(char *path, char *dest)
1377 char *dest_path;
1378 struct stat info;
1379 struct stat dest_info;
1380 gboolean retval = TRUE;
1382 check_flags();
1384 dest_path = make_dest_path(path, dest);
1386 if (mc_lstat(path, &info))
1388 send_error();
1389 return FALSE;
1392 if (mc_lstat(dest_path, &dest_info) == 0)
1394 int err;
1395 gboolean merge;
1397 merge = S_ISDIR(info.st_mode) && S_ISDIR(dest_info.st_mode);
1399 g_string_sprintf(message, _("?'%s' already exists - %s?"),
1400 dest_path,
1401 merge ? _("merge contents") : _("overwrite"));
1403 if (!reply(from_parent, TRUE))
1404 return FALSE;
1406 if (!merge)
1408 if (S_ISDIR(dest_info.st_mode))
1409 err = rmdir(dest_path);
1410 else
1411 err = unlink(dest_path);
1413 if (err)
1415 send_error();
1416 if (errno != ENOENT)
1417 return FALSE;
1418 g_string_sprintf(message,
1419 _("'Trying copy anyway...\n"));
1420 send();
1424 else if (!quiet)
1426 g_string_sprintf(message,
1427 _("?Copy %s as %s?"), path, dest_path);
1428 if (!reply(from_parent, FALSE))
1429 return FALSE;
1431 else
1433 g_string_sprintf(message, _("'Copying %s as %s\n"), path,
1434 dest_path);
1435 send();
1438 if (S_ISDIR(info.st_mode))
1440 char *safe_path, *safe_dest;
1441 struct stat dest_info;
1442 gboolean exists;
1444 /* (we will do the update ourselves now, rather than
1445 * afterwards)
1447 retval = FALSE;
1449 safe_path = g_strdup(path);
1450 safe_dest = g_strdup(dest_path);
1452 exists = !mc_lstat(dest_path, &dest_info);
1454 if (exists && !S_ISDIR(dest_info.st_mode))
1456 g_string_sprintf(message,
1457 _("!ERROR: Destination already exists, "
1458 "but is not a directory\n"));
1460 else if (exists == FALSE && mkdir(dest_path, info.st_mode))
1461 send_error();
1462 else
1464 if (!exists)
1466 /* (just been created then) */
1467 g_string_sprintf(message, "+%s", dest);
1468 send();
1471 action_leaf = NULL;
1472 for_dir_contents(do_copy2, safe_path, safe_dest);
1473 /* Note: dest_path now invalid... */
1476 g_free(safe_path);
1477 g_free(safe_dest);
1479 else if (S_ISLNK(info.st_mode))
1481 char target[MAXPATHLEN + 1];
1482 int count;
1484 /* Not all versions of cp(1) can make symlinks,
1485 * so we special-case it.
1488 count = readlink(path, target, sizeof(target) - 1);
1489 if (count < 0)
1491 send_error();
1492 retval = FALSE;
1494 else
1496 target[count] = '\0';
1497 if (symlink(target, dest_path))
1499 send_error();
1500 retval = FALSE;
1504 else
1506 guchar *error;
1508 error = copy_file(path, dest_path);
1510 if (error)
1512 g_string_sprintf(message, _("!ERROR: %s\n"), error);
1513 send();
1514 retval = FALSE;
1518 return retval;
1521 /* If action_leaf is not NULL it specifies the new leaf name */
1522 static gboolean do_move2(char *path, char *dest)
1524 char *dest_path;
1525 gboolean retval = TRUE;
1526 char *argv[] = {"mv", "-f", NULL, NULL, NULL};
1527 struct stat info2;
1528 gboolean is_dir;
1530 check_flags();
1532 dest_path = make_dest_path(path, dest);
1534 is_dir = mc_lstat(path, &info2) == 0 && S_ISDIR(info2.st_mode);
1536 if (access(dest_path, F_OK) == 0)
1538 struct stat info;
1539 int err;
1541 g_string_sprintf(message,
1542 _("?'%s' already exists - overwrite?"),
1543 dest_path);
1544 if (!reply(from_parent, TRUE))
1545 return FALSE;
1547 if (mc_lstat(dest_path, &info))
1549 send_error();
1550 return FALSE;
1553 if (S_ISDIR(info.st_mode))
1554 err = rmdir(dest_path);
1555 else
1556 err = unlink(dest_path);
1558 if (err)
1560 send_error();
1561 if (errno != ENOENT)
1562 return FALSE;
1563 g_string_sprintf(message,
1564 _("'Trying move anyway...\n"));
1565 send();
1568 else if (!quiet)
1570 g_string_sprintf(message,
1571 _("?Move %s as %s?"), path, dest_path);
1572 if (!reply(from_parent, FALSE))
1573 return FALSE;
1575 else
1577 g_string_sprintf(message, _("'Moving %s as %s\n"), path,
1578 dest_path);
1579 send();
1582 argv[2] = path;
1583 argv[3] = dest_path;
1585 if (fork_exec_wait(argv) == 0)
1587 char *leaf;
1589 leaf = strrchr(dest_path, '/');
1590 if (!leaf)
1591 leaf = dest_path; /* Error? */
1592 else
1593 leaf++;
1595 g_string_sprintf(message, "+%s", path);
1596 g_string_truncate(message, leaf - dest_path);
1597 send();
1598 if (is_dir) {
1599 g_string_sprintf(message, "m%s", path);
1600 send();
1603 else
1605 g_string_sprintf(message,
1606 _("!ERROR: Failed to move %s as %s\n"),
1607 path, dest_path);
1608 send();
1609 retval = FALSE;
1612 return retval;
1615 /* Copy path to dest.
1616 * Check that path not copied into itself.
1618 static gboolean do_copy(char *path, char *dest)
1620 if (is_sub_dir(make_dest_path(path, dest), path))
1622 g_string_sprintf(message,
1623 _("!ERROR: Can't copy object into itself\n"));
1624 send();
1625 return FALSE;
1627 return do_copy2(path, dest);
1630 /* Move path to dest.
1631 * Check that path not moved into itself.
1633 static gboolean do_move(char *path, char *dest)
1635 if (is_sub_dir(make_dest_path(path, dest), path))
1637 g_string_sprintf(message,
1638 _("!ERROR: Can't move/rename object into itself\n"));
1639 send();
1640 return FALSE;
1642 return do_move2(path, dest);
1645 static gboolean do_link(char *path, char *dest)
1647 char *dest_path;
1648 char *leaf;
1650 check_flags();
1652 leaf = strrchr(path, '/');
1653 if (!leaf)
1654 leaf = path; /* Error? */
1655 else
1656 leaf++;
1658 dest_path = make_path(dest, leaf)->str;
1660 if (quiet)
1662 g_string_sprintf(message, _("'Linking %s as %s\n"), path,
1663 dest_path);
1664 send();
1666 else
1668 g_string_sprintf(message,
1669 _("?Link %s as %s?"), path, dest_path);
1670 if (!reply(from_parent, FALSE))
1671 return FALSE;
1674 if (symlink(path, dest_path))
1676 send_error();
1677 return FALSE;
1680 return TRUE;
1683 /* Mount/umount this item (depending on 'mount') */
1684 static void do_mount(guchar *path, gboolean mount)
1686 char *argv[3] = {NULL, NULL, NULL};
1688 check_flags();
1690 argv[0] = mount ? "mount" : "umount";
1691 argv[1] = path;
1693 if (quiet)
1695 g_string_sprintf(message,
1696 mount ? _("'Mounting %s\n")
1697 : _("'Unmounting %s\n"),
1698 path);
1699 send();
1701 else
1703 g_string_sprintf(message,
1704 mount ? _("?Mount %s?\n")
1705 : _("?Unmount %s?\n"),
1706 path);
1707 if (!reply(from_parent, FALSE))
1708 return;
1711 if (fork_exec_wait(argv) == 0)
1713 g_string_sprintf(message, "m%s", path);
1714 send();
1716 else
1718 g_string_sprintf(message, _("!ERROR: Mount failed\n"));
1719 send();
1723 /* CHILD MAIN LOOPS */
1725 /* After forking, the child calls one of these functions */
1727 static void usage_cb(gpointer data)
1729 FilerWindow *filer_window = (FilerWindow *) data;
1730 Collection *collection = filer_window->collection;
1731 DirItem *item;
1732 int left = collection->number_selected;
1733 int i = -1;
1734 off_t total_size = 0;
1736 send_dir(filer_window->path);
1738 while (left > 0)
1740 i++;
1741 if (!collection->items[i].selected)
1742 continue;
1743 item = (DirItem *) collection->items[i].data;
1744 size_tally = 0;
1745 do_usage(make_path(filer_window->path,
1746 item->leafname)->str,
1747 filer_window->path);
1748 g_string_sprintf(message, "'%s: %s\n",
1749 item->leafname,
1750 format_size((unsigned long) size_tally));
1751 send();
1752 total_size += size_tally;
1753 left--;
1756 g_string_sprintf(message, _("'\nTotal: %s\n"),
1757 format_size((unsigned long) total_size));
1758 send();
1761 #ifdef DO_MOUNT_POINTS
1762 static void mount_cb(gpointer data)
1764 GList *paths = (GList *) data;
1765 gboolean mount_points = FALSE;
1767 for (; paths; paths = paths->next)
1769 guchar *path = (guchar *) paths->data;
1771 if (g_hash_table_lookup(mtab_mounts, path))
1772 do_mount(path, FALSE); /* Unmount */
1773 else if (g_hash_table_lookup(fstab_mounts, path))
1774 do_mount(path, TRUE); /* Mount */
1775 else
1776 continue;
1778 mount_points = TRUE;
1781 g_string_sprintf(message,
1782 mount_points ? _("'\nDone\n")
1783 : _("!No mount points selected!\n"));
1784 send();
1786 #endif
1788 static void delete_cb(gpointer data)
1790 FilerWindow *filer_window = (FilerWindow *) data;
1791 Collection *collection = filer_window->collection;
1792 DirItem *item;
1793 int left = collection->number_selected;
1794 int i = -1;
1796 send_dir(filer_window->path);
1798 while (left > 0)
1800 i++;
1801 if (!collection->items[i].selected)
1802 continue;
1803 item = (DirItem *) collection->items[i].data;
1804 if (do_delete(make_path(filer_window->path,
1805 item->leafname)->str,
1806 filer_window->path))
1808 g_string_sprintf(message, "+%s", filer_window->path);
1809 send();
1811 left--;
1814 g_string_sprintf(message, _("'\nDone\n"));
1815 send();
1818 static void find_cb(gpointer data)
1820 FilerWindow *filer_window = (FilerWindow *) data;
1821 Collection *collection = filer_window->collection;
1822 DirItem *item;
1823 int left = collection->number_selected;
1824 int i = -1;
1826 send_dir(filer_window->path);
1828 while (left > 0)
1830 i++;
1831 if (!collection->items[i].selected)
1832 continue;
1833 item = (DirItem *) collection->items[i].data;
1834 do_find(make_path(filer_window->path,
1835 item->leafname)->str,
1836 NULL);
1837 left--;
1840 g_string_sprintf(message, _("'\nDone\n"));
1841 send();
1844 static void chmod_cb(gpointer data)
1846 FilerWindow *filer_window = (FilerWindow *) data;
1847 Collection *collection = filer_window->collection;
1848 DirItem *item;
1849 int left = collection->number_selected;
1850 int i = -1;
1852 send_dir(filer_window->path);
1854 while (left > 0)
1856 i++;
1857 if (!collection->items[i].selected)
1858 continue;
1859 item = (DirItem *) collection->items[i].data;
1860 if (item->flags & ITEM_FLAG_SYMLINK)
1862 g_string_sprintf(message,
1863 _("!'%s' is a symbolic link\n"),
1864 item->leafname);
1865 send();
1867 else if (do_chmod(make_path(filer_window->path,
1868 item->leafname)->str, NULL))
1870 g_string_sprintf(message, "+%s", filer_window->path);
1871 send();
1873 left--;
1876 g_string_sprintf(message, _("'\nDone\n"));
1877 send();
1880 static void list_cb(gpointer data)
1882 GSList *paths = (GSList *) data;
1884 while (paths)
1886 send_dir((char *) paths->data);
1888 if (action_do_func((char *) paths->data, action_dest))
1890 g_string_sprintf(message, "+%s", action_dest);
1891 send();
1894 paths = paths->next;
1897 g_string_sprintf(message, _("'\nDone\n"));
1898 send();
1901 static void add_toggle(GUIside *gui_side, guchar *label, guchar *code)
1903 GtkWidget *check;
1905 check = gtk_check_button_new_with_label(label);
1906 gtk_object_set_data(GTK_OBJECT(check), "send-code", code);
1907 gtk_signal_connect(GTK_OBJECT(check), "clicked",
1908 button_reply, gui_side);
1909 gtk_box_pack_start(GTK_BOX(gui_side->vbox), check, FALSE, TRUE, 0);
1913 /* EXTERNAL INTERFACE */
1915 void action_find(FilerWindow *filer_window)
1917 GUIside *gui_side;
1918 Collection *collection;
1919 GtkWidget *hbox, *label, *scroller;
1920 gchar *titles[2];
1922 titles[0] = _("Name");
1923 titles[1] = _("Directory");
1925 collection = filer_window->collection;
1927 if (collection->number_selected < 1)
1929 report_error(PROJECT, _("You need to select some items "
1930 "to search through"));
1931 return;
1934 if (!last_find_string)
1935 last_find_string = g_strdup("'core'");
1937 new_entry_string = last_find_string;
1938 gui_side = start_action(filer_window, find_cb, FALSE);
1939 if (!gui_side)
1940 return;
1942 gui_side->show_info = TRUE;
1944 scroller = gtk_scrolled_window_new(NULL, NULL);
1945 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroller),
1946 GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
1947 gtk_box_pack_start(GTK_BOX(gui_side->vbox), scroller, TRUE, TRUE, 4);
1948 gui_side->results = gtk_clist_new_with_titles(
1949 sizeof(titles) / sizeof(*titles), titles);
1950 gtk_clist_column_titles_passive(GTK_CLIST(gui_side->results));
1951 gtk_widget_set_usize(gui_side->results, 100, 100);
1952 gtk_clist_set_column_width(GTK_CLIST(gui_side->results), 0, 100);
1953 gtk_clist_set_selection_mode(GTK_CLIST(gui_side->results),
1954 GTK_SELECTION_SINGLE);
1955 gtk_container_add(GTK_CONTAINER(scroller), gui_side->results);
1956 gtk_box_set_child_packing(GTK_BOX(gui_side->vbox),
1957 gui_side->log_hbox, FALSE, TRUE, 4, GTK_PACK_START);
1958 gtk_signal_connect(GTK_OBJECT(gui_side->results), "select_row",
1959 GTK_SIGNAL_FUNC(select_row_callback), gui_side);
1961 hbox = gtk_hbox_new(FALSE, 0);
1962 label = gtk_label_new(_("Expression:"));
1963 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, TRUE, 4);
1964 gui_side->default_string = &last_find_string;
1965 gui_side->entry = gtk_entry_new();
1966 gtk_widget_set_style(gui_side->entry, fixed_style);
1967 gtk_entry_set_text(GTK_ENTRY(gui_side->entry), last_find_string);
1968 gtk_editable_select_region(GTK_EDITABLE(gui_side->entry), 0, -1);
1969 gtk_widget_set_sensitive(gui_side->entry, FALSE);
1970 gtk_box_pack_start(GTK_BOX(hbox), gui_side->entry, TRUE, TRUE, 4);
1971 gtk_signal_connect(GTK_OBJECT(gui_side->entry), "changed",
1972 entry_changed, gui_side);
1973 gtk_box_pack_start(GTK_BOX(gui_side->vbox), hbox, FALSE, TRUE, 4);
1974 gtk_box_pack_start(GTK_BOX(hbox),
1975 new_help_button(show_condition_help, NULL),
1976 FALSE, TRUE, 4);
1978 gtk_window_set_title(GTK_WINDOW(gui_side->window), _("Find"));
1979 gtk_window_set_focus(GTK_WINDOW(gui_side->window), gui_side->entry);
1980 gtk_signal_connect_object(GTK_OBJECT(gui_side->entry), "activate",
1981 gtk_button_clicked, GTK_OBJECT(gui_side->quiet));
1982 number_of_windows++;
1983 gtk_widget_show_all(gui_side->window);
1986 /* Count disk space used by selected items */
1987 void action_usage(FilerWindow *filer_window)
1989 GUIside *gui_side;
1990 Collection *collection;
1992 collection = filer_window->collection;
1994 if (collection->number_selected < 1)
1996 report_error(PROJECT,
1997 _("You need to select some items to count"));
1998 return;
2001 gui_side = start_action(filer_window, usage_cb, TRUE);
2002 if (!gui_side)
2003 return;
2005 gui_side->show_info = TRUE;
2007 gtk_window_set_title(GTK_WINDOW(gui_side->window), _("Disk Usage"));
2008 number_of_windows++;
2009 gtk_widget_show_all(gui_side->window);
2012 /* Mount/unmount listed items (paths).
2013 * Free the list after this function returns.
2015 void action_mount(GList *paths)
2017 #ifdef DO_MOUNT_POINTS
2018 GUIside *gui_side;
2020 gui_side = start_action(paths, mount_cb, o_auto_mount);
2021 if (!gui_side)
2022 return;
2024 gtk_window_set_title(GTK_WINDOW(gui_side->window),
2025 _("Mount / Unmount"));
2026 number_of_windows++;
2027 gtk_widget_show_all(gui_side->window);
2028 #else
2029 report_error(PROJECT,
2030 _("ROX-Filer does not yet support mount points on your "
2031 "system. Sorry."));
2032 #endif /* DO_MOUNT_POINTS */
2035 /* Deletes all selected items in the window */
2036 void action_delete(FilerWindow *filer_window)
2038 GUIside *gui_side;
2039 Collection *collection;
2041 collection = filer_window->collection;
2043 if (collection->number_selected < 1)
2045 report_error(PROJECT,
2046 _("You need to select some items to delete"));
2047 return;
2050 gui_side = start_action(filer_window, delete_cb, o_auto_delete);
2051 if (!gui_side)
2052 return;
2054 gtk_window_set_title(GTK_WINDOW(gui_side->window), _("Delete"));
2055 add_toggle(gui_side,
2056 _("Force - don't confirm deletion of non-writeable items"),
2057 "F");
2058 add_toggle(gui_side,
2059 _("Brief - only log directories being deleted"),
2060 "B");
2062 number_of_windows++;
2063 gtk_widget_show_all(gui_side->window);
2066 /* Change the permissions of the selected items */
2067 void action_chmod(FilerWindow *filer_window)
2069 GUIside *gui_side;
2070 Collection *collection;
2071 GtkWidget *hbox, *label, *combo;
2072 static GList *presets = NULL;
2074 collection = filer_window->collection;
2076 if (collection->number_selected < 1)
2078 report_error(PROJECT,
2079 _("You need to select the items "
2080 "whose permissions you want to change"));
2081 return;
2084 if (!presets)
2086 presets = g_list_append(presets,
2087 _("a+x (Make executable/searchable)"));
2088 presets = g_list_append(presets,
2089 _("a-x (Make non-executable/non-searchable)"));
2090 presets = g_list_append(presets,
2091 _("u+rw (Give owner read+write)"));
2092 presets = g_list_append(presets,
2093 _("go-rwx (Private - owner access only)"));
2094 presets = g_list_append(presets,
2095 _("go=u-w (Public access, not write)"));
2098 if (!last_chmod_string)
2099 last_chmod_string = g_strdup((guchar *) presets->data);
2100 new_entry_string = last_chmod_string;
2101 gui_side = start_action(filer_window, chmod_cb, FALSE);
2102 if (!gui_side)
2103 return;
2105 add_toggle(gui_side,
2106 _("Brief - don't list processed files"), "B");
2107 add_toggle(gui_side,
2108 _("Recurse - also change contents of subdirectories"), "R");
2110 hbox = gtk_hbox_new(FALSE, 0);
2111 label = gtk_label_new(_("Command:"));
2112 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, TRUE, 4);
2113 gui_side->default_string = &last_chmod_string;
2115 combo = gtk_combo_new();
2116 gtk_combo_disable_activate(GTK_COMBO(combo));
2117 gtk_combo_set_use_arrows_always(GTK_COMBO(combo), TRUE);
2118 gtk_combo_set_popdown_strings(GTK_COMBO(combo), presets);
2120 gui_side->entry = GTK_COMBO(combo)->entry;
2121 gtk_entry_set_text(GTK_ENTRY(gui_side->entry), last_chmod_string);
2122 gtk_editable_select_region(GTK_EDITABLE(gui_side->entry), 0, -1);
2123 gtk_widget_set_sensitive(gui_side->entry, FALSE);
2124 gtk_box_pack_start(GTK_BOX(hbox), combo, TRUE, TRUE, 4);
2125 gtk_signal_connect(GTK_OBJECT(gui_side->entry), "changed",
2126 entry_changed, gui_side);
2127 gtk_box_pack_start(GTK_BOX(gui_side->vbox), hbox, FALSE, TRUE, 0);
2128 gtk_box_pack_start(GTK_BOX(hbox),
2129 new_help_button(show_chmod_help, NULL),
2130 FALSE, TRUE, 4);
2132 gtk_window_set_focus(GTK_WINDOW(gui_side->window), gui_side->entry);
2133 gtk_window_set_title(GTK_WINDOW(gui_side->window), _("Permissions"));
2134 gtk_signal_connect_object(GTK_OBJECT(gui_side->entry), "activate",
2135 gtk_button_clicked, GTK_OBJECT(gui_side->yes));
2137 number_of_windows++;
2138 gtk_widget_show_all(gui_side->window);
2141 /* If leaf is NULL then the copy has the same name as the original */
2142 void action_copy(GSList *paths, char *dest, char *leaf)
2144 GUIside *gui_side;
2146 action_dest = dest;
2147 action_leaf = leaf;
2148 action_do_func = do_copy;
2149 gui_side = start_action(paths, list_cb, o_auto_copy);
2150 if (!gui_side)
2151 return;
2153 gtk_window_set_title(GTK_WINDOW(gui_side->window), _("Copy"));
2154 number_of_windows++;
2155 gtk_widget_show_all(gui_side->window);
2158 /* If leaf is NULL then the file is not renamed */
2159 void action_move(GSList *paths, char *dest, char *leaf)
2161 GUIside *gui_side;
2163 action_dest = dest;
2164 action_leaf = leaf;
2165 action_do_func = do_move;
2166 gui_side = start_action(paths, list_cb, o_auto_move);
2167 if (!gui_side)
2168 return;
2170 gtk_window_set_title(GTK_WINDOW(gui_side->window), _("Move"));
2171 number_of_windows++;
2172 gtk_widget_show_all(gui_side->window);
2175 void action_link(GSList *paths, char *dest)
2177 GUIside *gui_side;
2179 action_dest = dest;
2180 action_do_func = do_link;
2181 gui_side = start_action(paths, list_cb, o_auto_link);
2182 if (!gui_side)
2183 return;
2185 gtk_window_set_title(GTK_WINDOW(gui_side->window), _("Link"));
2186 number_of_windows++;
2187 gtk_widget_show_all(gui_side->window);
2190 void action_init(void)
2192 options_sections = g_slist_prepend(options_sections, &options);
2193 option_register("action_auto_quiet", action_auto_quiet);