r1066: Fixed some compiler warnings (Vincent Lef�vre).
[rox-filer.git] / ROX-Filer / src / action.c
blob867d5ece45536b47a00995ce69c147fc13d91c23
1 /*
2 * $Id$
4 * ROX-Filer, filer for the ROX desktop project
5 * Copyright (C) 2002, the ROX-Filer team.
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 /* Allow use of GtkText widget */
27 /* #define GTK_ENABLE_BROKEN */
29 #include "config.h"
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <ctype.h>
34 #include <sys/param.h>
35 #include <errno.h>
36 #include <signal.h>
37 #include <sys/time.h>
38 #include <utime.h>
40 #include "global.h"
42 #include "action.h"
43 #include "string.h"
44 #include "support.h"
45 #include "gui_support.h"
46 #include "filer.h"
47 #include "display.h"
48 #include "main.h"
49 #include "options.h"
50 #include "modechange.h"
51 #include "find.h"
52 #include "dir.h"
53 #include "icon.h"
54 #include "mount.h"
56 /* Parent->Child messages are one character each:
58 * Y/N Yes/No button clicked
59 * F Force deletion of non-writeable items
60 * Q Quiet toggled
61 * E Entry text changed
64 #define SENSITIVE_YESNO(gui_side, state) \
65 do { \
66 gtk_widget_set_sensitive((gui_side)->yes, state); \
67 gtk_widget_set_sensitive((gui_side)->no, state); \
68 if ((gui_side)->entry) \
69 gtk_widget_set_sensitive((gui_side)->entry, state);\
70 } while (0)
72 typedef struct _GUIside GUIside;
73 typedef void ActionChild(gpointer data);
74 typedef gboolean ForDirCB(char *path, char *dest_path);
76 struct _GUIside
78 int from_child; /* File descriptor */
79 FILE *to_child;
80 int input_tag; /* gdk_input_add() */
81 GtkWidget *log;
82 GtkWidget *vbox, *window, *dir, *log_hbox, *flag_box;
83 GtkWidget *quiet, *yes, *no, *quiet_flag;
84 int child; /* Process ID */
85 int errors;
86 gboolean show_info; /* For Disk Usage */
88 GtkWidget *entry; /* May be NULL */
89 guchar **default_string; /* Changed when the entry changes */
90 void (*entry_string_func)(GtkWidget *widget, guchar *string);
92 char *next_dir; /* NULL => no timer active */
93 gint next_timer;
95 /* Used by Find */
96 FilerWindow *preview;
97 GtkWidget *results;
100 /* These don't need to be in a structure because we fork() before
101 * using them again.
103 static gboolean mount_open_dir = FALSE;
104 static int from_parent = 0;
105 static FILE *to_parent = NULL;
106 static gboolean quiet = FALSE;
107 static GString *message = NULL;
108 static char *action_dest = NULL;
109 static char *action_leaf = NULL;
110 static gboolean (*action_do_func)(char *source, char *dest);
111 static double size_tally; /* For Disk Usage */
112 static unsigned long dir_counter; /* For Disk Usage */
113 static unsigned long file_counter; /* For Disk Usage */
115 static struct mode_change *mode_change = NULL; /* For Permissions */
116 static FindCondition *find_condition = NULL; /* For Find */
118 /* Only used by child */
119 static gboolean o_force = FALSE;
120 static gboolean o_brief = FALSE;
121 static gboolean o_recurse = FALSE;
123 /* Whenever the text in these boxes is changed we store a copy of the new
124 * string to be used as the default next time.
126 static guchar *last_chmod_string = NULL;
127 static guchar *last_find_string = NULL;
129 /* Set to one of the above before forking. This may change over a call to
130 * reply(). It is reset to NULL once the text is parsed.
132 static guchar *new_entry_string = NULL;
134 /* Static prototypes */
135 static gboolean send();
136 static gboolean send_error();
137 static gboolean send_dir(char *dir);
138 static gboolean read_exact(int source, char *buffer, ssize_t len);
139 static void do_mount(guchar *path, gboolean mount);
140 static GtkWidget *add_toggle(GUIside *gui_side,
141 guchar *label, guchar *tip,
142 guchar *code,
143 gboolean active);
144 static gboolean reply(int fd, gboolean ignore_quiet);
145 static gboolean remove_pinned_ok(GList *paths);
147 /* SUPPORT */
149 static void preview_closed(GtkWidget *window, GUIside *gui_side)
151 gui_side->preview = NULL;
154 static void select_row_callback(GtkWidget *widget,
155 gint row, gint column,
156 GdkEventButton *event,
157 GUIside *gui_side)
159 char *leaf, *dir;
161 gtk_clist_get_text(GTK_CLIST(gui_side->results), row, 0, &leaf);
162 gtk_clist_get_text(GTK_CLIST(gui_side->results), row, 1, &dir);
164 gtk_clist_unselect_row(GTK_CLIST(gui_side->results), row, column);
166 if (gui_side->preview)
168 if (strcmp(gui_side->preview->path, dir) == 0)
169 display_set_autoselect(gui_side->preview, leaf);
170 else
171 filer_change_to(gui_side->preview, dir, leaf);
172 return;
175 gui_side->preview = filer_opendir(dir, NULL);
176 if (gui_side->preview)
178 display_set_autoselect(gui_side->preview, leaf);
179 gtk_signal_connect(GTK_OBJECT(gui_side->preview->window),
180 "destroy",
181 GTK_SIGNAL_FUNC(preview_closed), gui_side);
186 /* This is called whenever the user edits the entry box (if any) - send the
187 * new string.
189 static void entry_changed(GtkEditable *entry, GUIside *gui_side)
191 guchar *text;
193 g_return_if_fail(gui_side->default_string != NULL);
195 text = gtk_editable_get_chars(entry, 0, -1);
197 if (gui_side->entry_string_func)
198 gui_side->entry_string_func(GTK_WIDGET(gui_side->entry), text);
200 g_free(*(gui_side->default_string));
201 *(gui_side->default_string) = text; /* Gets text's ref */
203 if (!gui_side->to_child)
204 return;
206 fputc('E', gui_side->to_child);
207 fputs(text, gui_side->to_child);
208 fputc('\n', gui_side->to_child);
209 fflush(gui_side->to_child);
212 void show_condition_help(gpointer data)
214 static GtkWidget *help = NULL;
216 if (!help)
218 GtkWidget *text, *vbox, *button, *hbox, *frame;
220 help = gtk_window_new(GTK_WINDOW_DIALOG);
221 #ifdef GTK2
222 gtk_window_set_type_hint(GTK_WINDOW(help),
223 GDK_WINDOW_TYPE_HINT_DIALOG);
224 #endif
225 gtk_container_set_border_width(GTK_CONTAINER(help), 10);
226 gtk_window_set_title(GTK_WINDOW(help),
227 _("Find expression reference"));
229 vbox = gtk_vbox_new(FALSE, 0);
230 gtk_container_add(GTK_CONTAINER(help), vbox);
232 frame = gtk_frame_new(_("Quick Start"));
233 text = gtk_label_new(
234 _("Just put the name of the file you're looking for in single quotes:\n"
235 "'index.html' (to find a file called 'index.html')"));
236 gtk_misc_set_padding(GTK_MISC(text), 4, 4);
237 gtk_label_set_justify(GTK_LABEL(text), GTK_JUSTIFY_LEFT);
238 gtk_container_add(GTK_CONTAINER(frame), text);
239 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 4);
241 frame = gtk_frame_new(_("Examples"));
242 text = gtk_label_new(
243 _("'*.htm', '*.html' (finds HTML files)\n"
244 "IsDir 'lib' (finds directories called 'lib')\n"
245 "IsReg 'core' (finds a regular file called 'core')\n"
246 "! (IsDir, IsReg) (is neither a directory nor a regular file)\n"
247 "mtime after 1 day ago and size > 1Mb (big, and recently modified)\n"
248 "'CVS' prune, isreg (a regular file not in CVS)\n"
249 "IsReg system(grep -q fred \"%\") (contains the word 'fred')"));
250 gtk_widget_set_name(text, "fixed-font");
251 gtk_misc_set_padding(GTK_MISC(text), 4, 4);
252 gtk_label_set_justify(GTK_LABEL(text), GTK_JUSTIFY_LEFT);
253 gtk_container_add(GTK_CONTAINER(frame), text);
254 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 4);
256 frame = gtk_frame_new(_("Simple Tests"));
257 text = gtk_label_new(
258 _("IsReg, IsLink, IsDir, IsChar, IsBlock, IsDev, IsPipe, IsSocket (types)\n"
259 "IsSUID, IsSGID, IsSticky, IsReadable, IsWriteable, IsExecutable (permissions)"
260 "\n"
261 "IsEmpty, IsMine\n"
262 "\n"
263 "A pattern in single quotes is a shell-style wildcard pattern to match. If it\n"
264 "contains a slash then the match is against the full path; otherwise it is \n"
265 "against the leafname only."));
266 gtk_misc_set_padding(GTK_MISC(text), 4, 4);
267 gtk_label_set_justify(GTK_LABEL(text), GTK_JUSTIFY_LEFT);
268 gtk_container_add(GTK_CONTAINER(frame), text);
269 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 4);
271 frame = gtk_frame_new(_("Comparisons"));
272 text = gtk_label_new(
273 _("<, <=, =, !=, >, >=, After, Before (compare two values)\n"
274 "5 bytes, 1Kb, 2Mb, 3Gb (file sizes)\n"
275 "2 secs|mins|hours|days|weeks|years ago|hence (times)\n"
276 "atime, ctime, mtime, now, size, inode, nlinks, uid, gid, blocks (values)"));
277 gtk_misc_set_padding(GTK_MISC(text), 4, 4);
278 gtk_label_set_justify(GTK_LABEL(text), GTK_JUSTIFY_LEFT);
279 gtk_container_add(GTK_CONTAINER(frame), text);
280 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 4);
282 frame = gtk_frame_new(_("Specials"));
283 text = gtk_label_new(
284 _("system(command) (true if 'command' returns with a zero exit status; a % \n"
285 "in 'command' is replaced with the path of the current file)\n"
286 "prune (false, and prevents searching the contents of a directory).")
288 gtk_misc_set_padding(GTK_MISC(text), 4, 4);
289 gtk_label_set_justify(GTK_LABEL(text), GTK_JUSTIFY_LEFT);
290 gtk_container_add(GTK_CONTAINER(frame), text);
291 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 4);
293 hbox = gtk_hbox_new(FALSE, 20);
294 gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 0);
296 text = gtk_label_new(
297 _("See the ROX-Filer manual for full details."));
298 gtk_box_pack_start(GTK_BOX(hbox), text, TRUE, TRUE, 0);
299 button = gtk_button_new_with_label(_("Close"));
300 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, TRUE, 0);
301 gtk_signal_connect_object(GTK_OBJECT(button), "clicked",
302 GTK_SIGNAL_FUNC(gtk_widget_hide), GTK_OBJECT(help));
304 gtk_signal_connect_object(GTK_OBJECT(help), "delete_event",
305 GTK_SIGNAL_FUNC(gtk_widget_hide), GTK_OBJECT(help));
308 if (GTK_WIDGET_VISIBLE(help))
309 gtk_widget_hide(help);
310 gtk_widget_show_all(help);
313 static void show_chmod_help(gpointer data)
315 static GtkWidget *help = NULL;
317 if (!help)
319 GtkWidget *text, *vbox, *button, *hbox, *sep;
321 help = gtk_window_new(GTK_WINDOW_DIALOG);
322 #ifdef GTK2
323 gtk_window_set_type_hint(GTK_WINDOW(help),
324 GDK_WINDOW_TYPE_HINT_DIALOG);
325 #endif
326 gtk_container_set_border_width(GTK_CONTAINER(help), 10);
327 gtk_window_set_title(GTK_WINDOW(help),
328 _("Permissions command reference"));
330 vbox = gtk_vbox_new(FALSE, 0);
331 gtk_container_add(GTK_CONTAINER(help), vbox);
333 text = gtk_label_new(
334 _("Normally, you can just select a command from the menu (click \n"
335 "on the arrow beside the command box). Sometimes, you need more...\n"
336 "\n"
337 "The format of a command is:\n"
338 "CHANGE, CHANGE, ...\n"
339 "Each CHANGE is:\n"
340 "WHO HOW PERMISSIONS\n"
341 "WHO is some combination of u, g and o which determines whether to\n"
342 "change the permissions for the User (owner), Group or Others.\n"
343 "HOW is +, - or = to add, remove or set exactly the permissions.\n"
344 "PERMISSIONS is some combination of the letters 'rwxXstugo'\n\n"
346 "Bracketed text and spaces are ignored.\n\n"
348 "Examples:\n"
349 "u+rw (the file owner gains read and write permission)\n"
350 "g=u (the group permissions are set to be the same as the user's)\n"
351 "o=u-w (others get the same permissions as the owner, but without "
352 "write permission)\n"
353 "a+x (everyone gets execute/access permission - same as 'ugo+x')\n"
354 "a+X (directories become accessable by everyone; files which were\n"
355 "executable by anyone become executable by everyone)\n"
356 "u+rw, go+r (two commands at once!)\n"
357 "u+s (set the SetUID bit - often has no effect on script files)\n"
358 "755 (set the permissions directly)\n"
360 "\nSee the chmod(1) man page for full details."));
361 gtk_label_set_justify(GTK_LABEL(text), GTK_JUSTIFY_LEFT);
362 gtk_box_pack_start(GTK_BOX(vbox), text, TRUE, TRUE, 0);
364 hbox = gtk_hbox_new(FALSE, 20);
365 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0);
367 sep = gtk_hseparator_new();
368 gtk_box_pack_start(GTK_BOX(hbox), sep, TRUE, TRUE, 0);
369 button = gtk_button_new_with_label(_("Close"));
370 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, TRUE, 0);
371 gtk_signal_connect_object(GTK_OBJECT(button), "clicked",
372 GTK_SIGNAL_FUNC(gtk_widget_hide), GTK_OBJECT(help));
374 gtk_signal_connect_object(GTK_OBJECT(help), "delete_event",
375 GTK_SIGNAL_FUNC(gtk_widget_hide), GTK_OBJECT(help));
378 if (GTK_WIDGET_VISIBLE(help))
379 gtk_widget_hide(help);
380 gtk_widget_show_all(help);
383 static gboolean display_dir(gpointer data)
385 GUIside *gui_side = (GUIside *) data;
387 gtk_label_set_text(GTK_LABEL(gui_side->dir), gui_side->next_dir + 1);
388 g_free(gui_side->next_dir);
389 gui_side->next_dir = NULL;
391 return FALSE;
394 static void add_to_results(GUIside *gui_side, gchar *path)
396 gchar *row[] = {"Leaf", "Dir"};
397 gchar *slash;
398 int len;
400 slash = strrchr(path, '/');
401 g_return_if_fail(slash != NULL);
403 len = slash - path;
404 row[1] = g_strndup(path, MAX(len, 1));
405 row[0] = slash + 1;
407 gtk_clist_append(GTK_CLIST(gui_side->results), row);
409 g_free(row[1]);
412 /* Called when the child sends us a message */
413 static void message_from_child(gpointer data,
414 gint source,
415 GdkInputCondition condition)
417 char buf[5];
418 GUIside *gui_side = (GUIside *) data;
419 #ifdef GTK2
420 GtkTextBuffer *text_buffer;
421 GtkTextIter end;
423 text_buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(gui_side->log));
424 #endif
426 if (read_exact(source, buf, 4))
428 ssize_t message_len;
429 char *buffer;
431 buf[4] = '\0';
432 message_len = strtol(buf, NULL, 16);
433 buffer = g_malloc(message_len + 1);
434 if (message_len > 0 && read_exact(source, buffer, message_len))
436 buffer[message_len] = '\0';
437 if (*buffer == '?')
439 /* Ask a question */
440 SENSITIVE_YESNO(gui_side, TRUE);
441 gtk_window_set_focus(
442 GTK_WINDOW(gui_side->window),
443 gui_side->entry ? gui_side->entry
444 : gui_side->yes);
446 else if (*buffer == '+')
448 /* Update/rescan this item */
449 refresh_dirs(buffer + 1);
450 g_free(buffer);
451 return;
453 else if (*buffer == '=')
455 /* Add to search results */
456 add_to_results(gui_side, buffer + 1);
457 g_free(buffer);
458 return;
460 else if (*buffer == '#')
462 /* Clear search results area */
463 gtk_clist_clear(GTK_CLIST(gui_side->results));
464 g_free(buffer);
465 return;
468 else if (*buffer == 'm' || *buffer == 'M')
470 /* Mount / major changes to this path */
471 if (*buffer == 'M')
472 mount_update(TRUE);
473 filer_check_mounted(buffer + 1);
474 g_free(buffer);
475 return;
477 else if (*buffer == '/')
479 /* Update the current object display */
480 if (gui_side->next_dir)
481 g_free(gui_side->next_dir);
482 else
483 gui_side->next_timer =
484 gtk_timeout_add(500,
485 display_dir,
486 gui_side);
487 gui_side->next_dir = buffer;
488 return;
490 else if (*buffer == 'o')
492 /* Open a filer window */
493 filer_opendir(buffer + 1, NULL);
494 g_free(buffer);
495 return;
497 else if (*buffer == '!')
498 gui_side->errors++;
500 #ifdef GTK2
501 gtk_text_buffer_get_end_iter(text_buffer, &end);
502 gtk_text_buffer_insert_with_tags_by_name(text_buffer,
503 &end, buffer + 1, message_len - 1,
504 *buffer == '!' ? "red" : NULL, NULL);
505 /* Causes a crash sometimes!
506 gtk_text_view_scroll_to_iter(
507 GTK_TEXT_VIEW(gui_side->log),
508 &end,
509 0.0, FALSE, 0, 0);
511 #else
512 gtk_text_insert(GTK_TEXT(gui_side->log),
513 NULL,
514 *buffer == '!' ? &red : NULL,
515 NULL,
516 buffer + 1, message_len - 1);
517 #endif
518 g_free(buffer);
519 return;
521 g_printerr("Child died in the middle of a message.\n");
524 /* The child is dead */
525 gui_side->child = 0;
527 fclose(gui_side->to_child);
528 gui_side->to_child = NULL;
529 close(gui_side->from_child);
530 gdk_input_remove(gui_side->input_tag);
531 gtk_widget_set_sensitive(gui_side->quiet, FALSE);
533 if (gui_side->errors)
535 guchar *report;
537 if (gui_side->errors == 1)
538 report = g_strdup(_("There was one error.\n"));
539 else
540 report = g_strdup_printf(_("There were %d errors.\n"),
541 gui_side->errors);
543 #ifdef GTK2
544 gtk_text_buffer_insert_at_cursor(text_buffer, report, -1);
545 #else
546 gtk_text_insert(GTK_TEXT(gui_side->log), NULL, &red, NULL,
547 report, -1);
548 #endif
550 g_free(report);
552 else if (gui_side->show_info == FALSE)
553 gtk_widget_destroy(gui_side->window);
556 /* Scans src_dir, updating dest_path whenever cb returns TRUE */
557 static void for_dir_contents(ForDirCB *cb, char *src_dir, char *dest_path)
559 DIR *d;
560 struct dirent *ent;
561 GList *list = NULL, *next;
563 d = mc_opendir(src_dir);
564 if (!d)
566 /* Message displayed is "ERROR reading 'path': message" */
567 g_string_sprintf(message, "!%s '%s': %s\n",
568 _("ERROR reading"),
569 src_dir, g_strerror(errno));
570 send();
571 return;
574 send_dir(src_dir);
576 while ((ent = mc_readdir(d)))
578 if (ent->d_name[0] == '.' && (ent->d_name[1] == '\0'
579 || (ent->d_name[1] == '.' && ent->d_name[2] == '\0')))
580 continue;
581 list = g_list_append(list, g_strdup(make_path(src_dir,
582 ent->d_name)->str));
584 mc_closedir(d);
586 if (!list)
587 return;
589 next = list;
591 while (next)
593 if (cb((char *) next->data, dest_path))
595 g_string_sprintf(message, "+%s", dest_path);
596 send();
599 g_free(next->data);
600 next = next->next;
602 g_list_free(list);
603 return;
606 /* Read this many bytes into the buffer. TRUE on success. */
607 static gboolean read_exact(int source, char *buffer, ssize_t len)
609 while (len > 0)
611 ssize_t got;
612 got = read(source, buffer, len);
613 if (got < 1)
614 return FALSE;
615 len -= got;
616 buffer += got;
618 return TRUE;
621 /* Send 'message' to our parent process. TRUE on success. */
622 static gboolean send(void)
624 char len_buffer[5];
625 ssize_t len;
627 g_return_val_if_fail(message->len < 0xffff, FALSE);
629 sprintf(len_buffer, "%04x", message->len);
630 fwrite(len_buffer, 1, 4, to_parent);
631 len = fwrite(message->str, 1, message->len, to_parent);
632 fflush(to_parent);
633 return len == message->len;
636 /* Set the directory indicator at the top of the window */
637 static gboolean send_dir(char *dir)
639 g_string_sprintf(message, "/%s", dir);
640 return send();
643 static gboolean send_error(void)
645 g_string_sprintf(message, "!%s: %s\n", _("ERROR"), g_strerror(errno));
646 return send();
649 static void quiet_clicked(GtkWidget *button, GUIside *gui_side)
651 GtkToggleButton *quiet_flag = GTK_TOGGLE_BUTTON(gui_side->quiet_flag);
653 if (!gui_side->to_child)
654 return;
656 gtk_widget_set_sensitive(gui_side->quiet, FALSE);
658 if (gtk_toggle_button_get_active(quiet_flag))
659 return; /* (shouldn't happen) */
661 gtk_toggle_button_set_active(quiet_flag, TRUE);
663 if (GTK_WIDGET_SENSITIVE(gui_side->yes))
664 gtk_button_clicked(GTK_BUTTON(gui_side->yes));
667 /* Send 'Quiet' if possible, 'Yes' otherwise */
668 static void find_return_pressed(GtkWidget *button, GUIside *gui_side)
670 if (GTK_WIDGET_SENSITIVE(gui_side->quiet))
671 gtk_button_clicked(GTK_BUTTON(gui_side->quiet));
672 else if (GTK_WIDGET_SENSITIVE(gui_side->yes))
673 gtk_button_clicked(GTK_BUTTON(gui_side->yes));
674 else
675 gdk_beep();
678 static void button_reply(GtkWidget *button, GUIside *gui_side)
680 char *text;
682 if (!gui_side->to_child)
683 return;
685 text = gtk_object_get_data(GTK_OBJECT(button), "send-code");
686 g_return_if_fail(text != NULL);
687 fputc(*text, gui_side->to_child);
688 fflush(gui_side->to_child);
690 if (*text == 'Y' || *text == 'N')
691 SENSITIVE_YESNO(gui_side, FALSE);
692 if (*text == 'Q')
694 gtk_widget_set_sensitive(gui_side->quiet,
695 !gtk_toggle_button_get_active(
696 GTK_TOGGLE_BUTTON(gui_side->quiet_flag)));
700 static void read_new_entry_text(void)
702 int len;
703 char c;
704 GString *new;
706 new = g_string_new(NULL);
708 for (;;)
710 len = read(from_parent, &c, 1);
711 if (len != 1)
713 fprintf(stderr, "read() error: %s\n",
714 g_strerror(errno));
715 _exit(1); /* Parent died? */
718 if (c == '\n')
719 break;
720 g_string_append_c(new, c);
723 g_free(new_entry_string);
724 new_entry_string = new->str;
725 g_string_free(new, FALSE);
728 static void process_flag(char flag)
730 switch (flag)
732 case 'Q':
733 quiet = !quiet;
734 break;
735 case 'F':
736 o_force = !o_force;
737 break;
738 case 'R':
739 o_recurse = !o_recurse;
740 break;
741 case 'B':
742 o_brief = !o_brief;
743 break;
744 case 'E':
745 read_new_entry_text();
746 break;
747 default:
748 g_string_sprintf(message,
749 "!ERROR: Bad message '%c'\n", flag);
750 send();
751 break;
755 /* If the parent has sent any flag toggles, read them */
756 static void check_flags(void)
758 fd_set set;
759 int got;
760 char retval;
761 struct timeval tv;
763 FD_ZERO(&set);
765 while (1)
767 FD_SET(from_parent, &set);
768 tv.tv_sec = 0;
769 tv.tv_usec = 0;
770 got = select(from_parent + 1, &set, NULL, NULL, &tv);
772 if (got == -1)
773 g_error("select() failed: %s\n", g_strerror(errno));
774 else if (!got)
775 return;
777 got = read(from_parent, &retval, 1);
778 if (got != 1)
779 g_error("read() error: %s\n", g_strerror(errno));
781 process_flag(retval);
785 /* Read until the user sends a reply. If ignore_quiet is TRUE then
786 * the user MUST click Yes or No, else treat quiet on as Yes.
787 * If the user needs prompting then does send().
789 static gboolean reply(int fd, gboolean ignore_quiet)
791 ssize_t len;
792 char retval;
794 if (quiet && !ignore_quiet)
795 return TRUE;
797 send();
799 while (1)
801 len = read(fd, &retval, 1);
802 if (len != 1)
804 fprintf(stderr, "read() error: %s\n",
805 g_strerror(errno));
806 _exit(1); /* Parent died? */
809 switch (retval)
811 case 'Y':
812 g_string_sprintf(message, "' %s\n", _("Yes"));
813 send();
814 return TRUE;
815 case 'N':
816 g_string_sprintf(message, "' %s\n", _("No"));
817 send();
818 return FALSE;
819 default:
820 process_flag(retval);
821 break;
826 static void destroy_action_window(GtkWidget *widget, gpointer data)
828 GUIside *gui_side = (GUIside *) data;
830 if (gui_side->child)
832 kill(gui_side->child, SIGTERM);
833 fclose(gui_side->to_child);
834 close(gui_side->from_child);
835 gdk_input_remove(gui_side->input_tag);
838 if (gui_side->next_dir)
840 gtk_timeout_remove(gui_side->next_timer);
841 g_free(gui_side->next_dir);
844 if (gui_side->preview)
846 gtk_signal_disconnect_by_data(
847 GTK_OBJECT(gui_side->preview->window),
848 gui_side);
849 gui_side->preview = NULL;
852 g_free(gui_side);
854 if (--number_of_windows < 1)
855 gtk_main_quit();
858 /* Create two pipes, fork() a child and return a pointer to a GUIside struct
859 * (NULL on failure). The child calls func().
861 * If autoq then automatically selects 'Quiet'.
863 static GUIside *start_action_with_options(gpointer data, ActionChild *func,
864 gboolean autoq,
865 int force, int brief, int recurse)
867 int filedes[4]; /* 0 and 2 are for reading */
868 GUIside *gui_side;
869 int child;
870 GtkWidget *vbox, *button, *scrollbar, *actions, *text;
871 struct sigaction act;
872 #ifdef GTK2
873 GtkWidget *frame;
874 #endif
876 if (pipe(filedes))
878 report_error("pipe: %s", g_strerror(errno));
879 return NULL;
882 if (pipe(filedes + 2))
884 close(filedes[0]);
885 close(filedes[1]);
886 report_error("pipe: %s", g_strerror(errno));
887 return NULL;
890 o_force = force;
891 o_brief = brief;
892 o_recurse = recurse;
894 child = fork();
895 switch (child)
897 case -1:
898 report_error("fork: %s", g_strerror(errno));
899 return NULL;
900 case 0:
901 /* We are the child */
903 quiet = autoq;
905 /* Reset the SIGCHLD handler */
906 act.sa_handler = SIG_DFL;
907 sigemptyset(&act.sa_mask);
908 act.sa_flags = 0;
909 sigaction(SIGCHLD, &act, NULL);
911 message = g_string_new(NULL);
912 close(filedes[0]);
913 close(filedes[3]);
914 to_parent = fdopen(filedes[1], "wb");
915 from_parent = filedes[2];
916 func(data);
917 send_dir("");
918 _exit(0);
921 /* We are the parent */
922 close(filedes[1]);
923 close(filedes[2]);
924 gui_side = g_malloc(sizeof(GUIside));
925 gui_side->from_child = filedes[0];
926 gui_side->to_child = fdopen(filedes[3], "wb");
927 gui_side->log = NULL;
928 gui_side->child = child;
929 gui_side->errors = 0;
930 gui_side->show_info = FALSE;
931 gui_side->preview = NULL;
932 gui_side->results = NULL;
933 gui_side->entry = NULL;
934 gui_side->default_string = NULL;
935 gui_side->entry_string_func = NULL;
937 gui_side->window = gtk_window_new(GTK_WINDOW_DIALOG);
938 #ifdef GTK2
939 gtk_window_set_type_hint(GTK_WINDOW(gui_side->window),
940 GDK_WINDOW_TYPE_HINT_DIALOG);
941 #endif
942 gtk_container_set_border_width(GTK_CONTAINER(gui_side->window), 2);
943 gtk_window_set_default_size(GTK_WINDOW(gui_side->window), 450, 200);
944 gtk_signal_connect(GTK_OBJECT(gui_side->window), "destroy",
945 GTK_SIGNAL_FUNC(destroy_action_window), gui_side);
947 gui_side->vbox = vbox = gtk_vbox_new(FALSE, 0);
948 gtk_container_add(GTK_CONTAINER(gui_side->window), vbox);
950 gui_side->dir = gtk_label_new(_("<dir>"));
951 gtk_widget_set_usize(gui_side->dir, 8, -1);
952 gui_side->next_dir = NULL;
953 gtk_misc_set_alignment(GTK_MISC(gui_side->dir), 0.5, 0.5);
954 gtk_box_pack_start(GTK_BOX(vbox), gui_side->dir, FALSE, TRUE, 0);
956 gui_side->log_hbox = gtk_hbox_new(FALSE, 0);
957 gtk_box_pack_start(GTK_BOX(vbox), gui_side->log_hbox, TRUE, TRUE, 4);
959 #ifdef GTK2
960 frame = gtk_frame_new(NULL);
961 gtk_box_pack_start(GTK_BOX(gui_side->log_hbox), frame, TRUE, TRUE, 0);
963 text = gtk_text_view_new();
964 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
965 gtk_container_add(GTK_CONTAINER(frame), text);
967 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD);
968 gui_side->log = text;
969 scrollbar = gtk_vscrollbar_new(NULL);
970 gtk_widget_set_scroll_adjustments(text, NULL,
971 gtk_range_get_adjustment(GTK_RANGE(scrollbar)));
972 gtk_text_buffer_create_tag(
973 gtk_text_view_get_buffer(GTK_TEXT_VIEW(gui_side->log)),
974 "red", "foreground", "red",
975 NULL);
976 gtk_text_view_set_editable(GTK_TEXT_VIEW(text), FALSE);
977 gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(text), FALSE);
978 #else
979 text = gui_side->log = gtk_text_new(NULL, NULL);
980 scrollbar = gtk_vscrollbar_new(GTK_TEXT(text)->vadj);
981 gtk_box_pack_start(GTK_BOX(gui_side->log_hbox), text, TRUE, TRUE, 0);
982 #endif
983 gtk_widget_set_usize(text, 400, 100);
985 gtk_box_pack_start(GTK_BOX(gui_side->log_hbox),
986 scrollbar, FALSE, TRUE, 0);
988 actions = gtk_hbox_new(TRUE, 4);
989 gtk_box_pack_start(GTK_BOX(vbox), actions, FALSE, TRUE, 0);
990 gui_side->flag_box = gtk_hbox_new(FALSE, 16);
991 gtk_box_pack_start(GTK_BOX(vbox), gui_side->flag_box, FALSE, TRUE, 2);
993 gui_side->quiet = button = gtk_button_new_with_label(_("Quiet"));
994 gtk_misc_set_padding(GTK_MISC(GTK_BIN(button)->child), 12, 0);
995 gtk_box_pack_start(GTK_BOX(gui_side->flag_box), button, FALSE, TRUE, 0);
996 gtk_signal_connect(GTK_OBJECT(button), "clicked",
997 GTK_SIGNAL_FUNC(quiet_clicked), gui_side);
999 gui_side->yes = button = gtk_button_new_with_label(_("Yes"));
1000 gtk_object_set_data(GTK_OBJECT(button), "send-code", "Y");
1001 gtk_box_pack_start(GTK_BOX(actions), button, TRUE, TRUE, 0);
1002 gtk_signal_connect(GTK_OBJECT(button), "clicked",
1003 GTK_SIGNAL_FUNC(button_reply), gui_side);
1004 gui_side->no = button = gtk_button_new_with_label(_("No"));
1005 gtk_object_set_data(GTK_OBJECT(button), "send-code", "N");
1006 gtk_box_pack_start(GTK_BOX(actions), button, TRUE, TRUE, 0);
1007 gtk_signal_connect(GTK_OBJECT(button), "clicked",
1008 GTK_SIGNAL_FUNC(button_reply), gui_side);
1009 SENSITIVE_YESNO(gui_side, FALSE);
1011 button = gtk_button_new_with_label(_("Abort"));
1012 gtk_box_pack_start(GTK_BOX(actions), button, TRUE, TRUE, 0);
1013 gtk_signal_connect_object(GTK_OBJECT(button), "clicked",
1014 GTK_SIGNAL_FUNC(gtk_widget_destroy),
1015 GTK_OBJECT(gui_side->window));
1017 gui_side->input_tag = gdk_input_add(gui_side->from_child,
1018 GDK_INPUT_READ,
1019 message_from_child,
1020 gui_side);
1022 gui_side->quiet_flag = add_toggle(gui_side,
1023 _("Quiet"), _("Don't confirm every operation"),
1024 "Q", autoq);
1025 gtk_widget_set_sensitive(gui_side->quiet, !autoq);
1027 return gui_side;
1030 static GUIside *start_action(gpointer data, ActionChild *func, gboolean autoq)
1032 return start_action_with_options(data, func, autoq,
1033 option_get_int("action_force"),
1034 option_get_int("action_brief"),
1035 option_get_int("action_recurse"));
1038 /* ACTIONS ON ONE ITEM */
1040 /* These may call themselves recursively, or ask questions, etc.
1041 * TRUE iff the directory containing dest_path needs to be rescanned.
1044 /* Updates the global size_tally, file_counter and dir_counter */
1045 static gboolean do_usage(char *src_path, char *unused)
1047 struct stat info;
1049 check_flags();
1051 if (mc_lstat(src_path, &info))
1053 g_string_sprintf(message, "'%s:\n", src_path);
1054 send();
1055 send_error();
1056 return FALSE;
1059 if (S_ISREG(info.st_mode) || S_ISLNK(info.st_mode))
1061 file_counter++;
1062 size_tally += info.st_size;
1064 else if (S_ISDIR(info.st_mode))
1066 dir_counter++;
1067 g_string_sprintf(message, _("?Count contents of %s?"),
1068 src_path);
1069 if (reply(from_parent, FALSE))
1071 char *safe_path;
1072 safe_path = g_strdup(src_path);
1073 for_dir_contents(do_usage, safe_path, safe_path);
1074 g_free(safe_path);
1077 else
1078 file_counter++;
1080 return FALSE;
1083 /* dest_path is the dir containing src_path */
1084 static gboolean do_delete(char *src_path, char *dest_path)
1086 struct stat info;
1087 gboolean write_prot;
1089 check_flags();
1091 if (mc_lstat(src_path, &info))
1093 send_error();
1094 return FALSE;
1097 write_prot = S_ISLNK(info.st_mode) ? FALSE
1098 : access(src_path, W_OK) != 0;
1099 if (write_prot || !quiet)
1101 g_string_sprintf(message, _("?Delete %s'%s'?"),
1102 write_prot ? _("WRITE-PROTECTED ") : " ",
1103 src_path);
1104 if (!reply(from_parent, write_prot && !o_force))
1105 return FALSE;
1107 else if (!o_brief)
1109 g_string_sprintf(message, _("'Deleting '%s'\n"), src_path);
1110 send();
1113 if (S_ISDIR(info.st_mode))
1115 char *safe_path;
1116 safe_path = g_strdup(src_path);
1117 for_dir_contents(do_delete, safe_path, safe_path);
1118 if (rmdir(safe_path))
1120 g_free(safe_path);
1121 send_error();
1122 return FALSE;
1124 g_string_sprintf(message, _("'Directory '%s' deleted\n"),
1125 safe_path);
1126 send();
1127 g_string_sprintf(message, "m%s", safe_path);
1128 send();
1129 g_free(safe_path);
1131 else if (unlink(src_path))
1133 send_error();
1134 return FALSE;
1137 return TRUE;
1140 /* path is the item to check. If is is a directory then we may recurse
1141 * (unless prune is used).
1143 static gboolean do_find(char *path, char *dummy)
1145 FindInfo info;
1146 char *slash;
1148 check_flags();
1150 if (!quiet)
1152 g_string_sprintf(message, _("?Check '%s'?"), path);
1153 if (!reply(from_parent, FALSE))
1154 return FALSE;
1157 for (;;)
1159 if (new_entry_string)
1161 if (find_condition)
1162 find_condition_free(find_condition);
1163 find_condition = find_compile(new_entry_string);
1164 g_free(new_entry_string);
1165 new_entry_string = NULL;
1168 if (find_condition)
1169 break;
1171 g_string_assign(message, _("!Invalid find condition - "
1172 "change it and try again\n"));
1173 send();
1174 g_string_sprintf(message, _("?Check '%s'?"), path);
1175 if (!reply(from_parent, TRUE))
1176 return FALSE;
1179 if (mc_lstat(path, &info.stats))
1181 send_error();
1182 g_string_sprintf(message, _("'(while checking '%s')\n"), path);
1183 send();
1184 return FALSE;
1187 info.fullpath = path;
1188 time(&info.now); /* XXX: Not for each check! */
1190 slash = strrchr(path, '/');
1191 info.leaf = slash ? slash + 1 : path;
1192 info.prune = FALSE;
1193 if (find_test_condition(find_condition, &info))
1195 g_string_sprintf(message, "=%s", path);
1196 send();
1199 if (S_ISDIR(info.stats.st_mode) && !info.prune)
1201 char *safe_path;
1202 safe_path = g_strdup(path);
1203 for_dir_contents(do_find, safe_path, safe_path);
1204 g_free(safe_path);
1207 return FALSE;
1210 /* Like mode_compile(), but ignores spaces and bracketed bits */
1211 struct mode_change *nice_mode_compile(const char *mode_string,
1212 unsigned int masked_ops)
1214 GString *new;
1215 int brackets = 0;
1216 struct mode_change *retval = NULL;
1218 new = g_string_new(NULL);
1220 for (; *mode_string; mode_string++)
1222 if (*mode_string == '(')
1223 brackets++;
1224 if (*mode_string == ')')
1226 brackets--;
1227 if (brackets < 0)
1228 break;
1229 continue;
1232 if (brackets == 0 && *mode_string != ' ')
1233 g_string_append_c(new, *mode_string);
1236 if (brackets == 0)
1237 retval = mode_compile(new->str, masked_ops);
1238 g_string_free(new, TRUE);
1239 return retval;
1242 static gboolean do_chmod(char *path, char *dummy)
1244 struct stat info;
1245 mode_t new_mode;
1247 check_flags();
1249 if (mc_lstat(path, &info))
1251 send_error();
1252 return FALSE;
1254 if (S_ISLNK(info.st_mode))
1255 return FALSE;
1257 if (!quiet)
1259 g_string_sprintf(message,
1260 _("?Change permissions of '%s'?"), path);
1261 if (!reply(from_parent, FALSE))
1262 return FALSE;
1264 else if (!o_brief)
1266 g_string_sprintf(message,
1267 _("'Changing permissions of '%s'\n"),
1268 path);
1269 send();
1272 for (;;)
1274 if (new_entry_string)
1276 if (mode_change)
1277 mode_free(mode_change);
1278 mode_change = nice_mode_compile(new_entry_string,
1279 MODE_MASK_ALL);
1280 g_free(new_entry_string);
1281 new_entry_string = NULL;
1284 if (mode_change)
1285 break;
1287 g_string_assign(message,
1288 _("!Invalid mode command - change it and try again\n"));
1289 send();
1290 g_string_sprintf(message,
1291 _("?Change permissions of '%s'?"), path);
1292 if (!reply(from_parent, TRUE))
1293 return FALSE;
1296 if (mc_lstat(path, &info))
1298 send_error();
1299 return FALSE;
1301 if (S_ISLNK(info.st_mode))
1302 return FALSE;
1304 new_mode = mode_adjust(info.st_mode, mode_change);
1305 if (chmod(path, new_mode))
1307 send_error();
1308 return FALSE;
1311 if (o_recurse && S_ISDIR(info.st_mode))
1313 guchar *safe_path;
1314 safe_path = g_strdup(path);
1315 for_dir_contents(do_chmod, safe_path, safe_path);
1316 g_free(safe_path);
1319 return TRUE;
1322 /* We want to copy 'object' into directory 'dir'. If 'action_leaf'
1323 * is set then that is the new leafname, otherwise the leafname stays
1324 * the same.
1326 static char *make_dest_path(char *object, char *dir)
1328 char *leaf;
1330 if (action_leaf)
1331 leaf = action_leaf;
1332 else
1334 leaf = strrchr(object, '/');
1335 if (!leaf)
1336 leaf = object; /* Error? */
1337 else
1338 leaf++;
1341 return make_path(dir, leaf)->str;
1344 /* If action_leaf is not NULL it specifies the new leaf name */
1345 static gboolean do_copy2(char *path, char *dest)
1347 char *dest_path;
1348 struct stat info;
1349 struct stat dest_info;
1350 gboolean retval = TRUE;
1352 check_flags();
1354 dest_path = make_dest_path(path, dest);
1356 if (mc_lstat(path, &info))
1358 send_error();
1359 return FALSE;
1362 if (mc_lstat(dest_path, &dest_info) == 0)
1364 int err;
1365 gboolean merge;
1367 merge = S_ISDIR(info.st_mode) && S_ISDIR(dest_info.st_mode);
1369 g_string_sprintf(message, _("?'%s' already exists - %s?"),
1370 dest_path,
1371 merge ? _("merge contents") : _("overwrite"));
1373 if (!reply(from_parent, TRUE))
1374 return FALSE;
1376 if (!merge)
1378 if (S_ISDIR(dest_info.st_mode))
1379 err = rmdir(dest_path);
1380 else
1381 err = unlink(dest_path);
1383 if (err)
1385 send_error();
1386 if (errno != ENOENT)
1387 return FALSE;
1388 g_string_sprintf(message,
1389 _("'Trying copy anyway...\n"));
1390 send();
1394 else if (!quiet)
1396 g_string_sprintf(message,
1397 _("?Copy %s as %s?"), path, dest_path);
1398 if (!reply(from_parent, FALSE))
1399 return FALSE;
1401 else
1403 g_string_sprintf(message, _("'Copying %s as %s\n"), path,
1404 dest_path);
1405 send();
1408 if (S_ISDIR(info.st_mode))
1410 mode_t mode = info.st_mode;
1411 char *safe_path, *safe_dest;
1412 struct stat dest_info;
1413 gboolean exists;
1415 /* (we will do the update ourselves now, rather than
1416 * afterwards)
1418 retval = FALSE;
1420 safe_path = g_strdup(path);
1421 safe_dest = g_strdup(dest_path);
1423 exists = !mc_lstat(dest_path, &dest_info);
1425 if (exists && !S_ISDIR(dest_info.st_mode))
1427 g_string_sprintf(message,
1428 _("!ERROR: Destination already exists, "
1429 "but is not a directory\n"));
1431 else if (exists == FALSE && mkdir(dest_path, 0700 | mode))
1432 send_error();
1433 else
1435 if (!exists)
1437 /* (just been created then) */
1438 g_string_sprintf(message, "+%s", dest);
1439 send();
1442 action_leaf = NULL;
1443 for_dir_contents(do_copy2, safe_path, safe_dest);
1444 /* Note: dest_path now invalid... */
1446 if (!exists)
1448 struct utimbuf utb;
1450 /* We may have created the directory with
1451 * more permissions than the source so that
1452 * we could write to it... change it back now.
1454 if (chmod(safe_dest, mode))
1455 send_error();
1457 /* Also, try to preserve the timestamps */
1458 utb.actime = info.st_atime;
1459 utb.modtime = info.st_mtime;
1461 utime(safe_dest, &utb);
1465 g_free(safe_path);
1466 g_free(safe_dest);
1468 else if (S_ISLNK(info.st_mode))
1470 char *target;
1472 /* Not all versions of cp(1) can make symlinks,
1473 * so we special-case it.
1476 target = readlink_dup(path);
1477 if (target)
1479 if (symlink(target, dest_path))
1481 send_error();
1482 retval = FALSE;
1485 g_free(target);
1487 else
1489 send_error();
1490 retval = FALSE;
1493 else
1495 guchar *error;
1497 error = copy_file(path, dest_path);
1499 if (error)
1501 g_string_sprintf(message, _("!%s\nFailed to copy '%s'"),
1502 error, path);
1503 g_free(error);
1504 send();
1505 retval = FALSE;
1509 return retval;
1512 /* If action_leaf is not NULL it specifies the new leaf name */
1513 static gboolean do_move2(char *path, char *dest)
1515 char *dest_path;
1516 gboolean retval = TRUE;
1517 char *argv[] = {"mv", "-f", NULL, NULL, NULL};
1518 struct stat info2;
1519 gboolean is_dir;
1520 char *err;
1522 check_flags();
1524 dest_path = make_dest_path(path, dest);
1526 is_dir = mc_lstat(path, &info2) == 0 && S_ISDIR(info2.st_mode);
1528 if (access(dest_path, F_OK) == 0)
1530 struct stat info;
1531 int err;
1533 g_string_sprintf(message,
1534 _("?'%s' already exists - overwrite?"),
1535 dest_path);
1536 if (!reply(from_parent, TRUE))
1537 return FALSE;
1539 if (mc_lstat(dest_path, &info))
1541 send_error();
1542 return FALSE;
1545 if (S_ISDIR(info.st_mode))
1546 err = rmdir(dest_path);
1547 else
1548 err = unlink(dest_path);
1550 if (err)
1552 send_error();
1553 if (errno != ENOENT)
1554 return FALSE;
1555 g_string_sprintf(message,
1556 _("'Trying move anyway...\n"));
1557 send();
1560 else if (!quiet)
1562 g_string_sprintf(message,
1563 _("?Move %s as %s?"), path, dest_path);
1564 if (!reply(from_parent, FALSE))
1565 return FALSE;
1567 else
1569 g_string_sprintf(message, _("'Moving %s as %s\n"), path,
1570 dest_path);
1571 send();
1574 argv[2] = path;
1575 argv[3] = dest_path;
1577 err = fork_exec_wait(argv);
1578 if (err)
1580 g_string_sprintf(message,
1581 _("!%s\nFailed to move %s as %s\n"),
1582 err, path, dest_path);
1583 send();
1584 retval = FALSE;
1585 g_free(err);
1587 else
1589 char *leaf;
1591 leaf = strrchr(path, '/');
1592 if (!leaf)
1593 leaf = path; /* Error? */
1594 else
1595 leaf++;
1597 g_string_sprintf(message, "+%s", path);
1598 g_string_truncate(message, leaf - path + 1);
1599 send();
1600 if (is_dir)
1602 g_string_sprintf(message, "m%s", path);
1603 send();
1607 return retval;
1610 /* Copy path to dest.
1611 * Check that path not copied into itself.
1613 static gboolean do_copy(char *path, char *dest)
1615 if (is_sub_dir(make_dest_path(path, dest), path))
1617 g_string_sprintf(message,
1618 _("!ERROR: Can't copy object into itself\n"));
1619 send();
1620 return FALSE;
1622 return do_copy2(path, dest);
1625 /* Move path to dest.
1626 * Check that path not moved into itself.
1628 static gboolean do_move(char *path, char *dest)
1630 if (is_sub_dir(make_dest_path(path, dest), path))
1632 g_string_sprintf(message,
1633 _("!ERROR: Can't move/rename object into itself\n"));
1634 send();
1635 return FALSE;
1637 return do_move2(path, dest);
1640 static gboolean do_link(char *path, char *dest)
1642 char *dest_path;
1644 check_flags();
1646 dest_path = make_dest_path(path, dest);
1648 if (quiet)
1650 g_string_sprintf(message, _("'Linking %s as %s\n"), path,
1651 dest_path);
1652 send();
1654 else
1656 g_string_sprintf(message,
1657 _("?Link %s as %s?"), path, dest_path);
1658 if (!reply(from_parent, FALSE))
1659 return FALSE;
1662 if (symlink(path, dest_path))
1664 send_error();
1665 return FALSE;
1668 return TRUE;
1671 /* Mount/umount this item (depending on 'mount') */
1672 static void do_mount(guchar *path, gboolean mount)
1674 char *argv[3] = {NULL, NULL, NULL};
1675 char *err;
1677 check_flags();
1679 argv[0] = mount ? "mount" : "umount";
1680 argv[1] = path;
1682 if (quiet)
1684 g_string_sprintf(message,
1685 mount ? _("'Mounting %s\n")
1686 : _("'Unmounting %s\n"),
1687 path);
1688 send();
1690 else
1692 g_string_sprintf(message,
1693 mount ? _("?Mount %s?\n")
1694 : _("?Unmount %s?\n"),
1695 path);
1696 if (!reply(from_parent, FALSE))
1697 return;
1700 err = fork_exec_wait(argv);
1701 if (err)
1703 g_string_sprintf(message, mount ?
1704 _("!%s\nMount failed\n") :
1705 _("!%s\nUnmount failed\n"), err);
1706 send();
1707 g_free(err);
1709 else
1711 g_string_sprintf(message, "M%s", path);
1712 send();
1713 if (mount && mount_open_dir)
1715 g_string_sprintf(message, "o%s", path);
1716 send();
1721 /* CHILD MAIN LOOPS */
1723 /* After forking, the child calls one of these functions */
1725 /* We use a double for total size in order to count beyond 4Gb */
1726 static void usage_cb(gpointer data)
1728 GList *paths = (GList *) data;
1729 double total_size = 0;
1730 gchar *tmp;
1732 dir_counter = file_counter = 0;
1734 for (; paths; paths = paths->next)
1736 guchar *path = (guchar *) paths->data;
1738 send_dir(path);
1740 size_tally = 0;
1742 do_usage(path, NULL);
1744 g_string_sprintf(message, "'%s: %s\n",
1745 g_basename(path),
1746 format_double_size(size_tally));
1747 send();
1748 total_size += size_tally;
1751 g_string_sprintf(message, _("'\nTotal: %s ("),
1752 format_double_size(total_size));
1754 if (file_counter)
1756 tmp = g_strdup_printf("%ld %s%s",
1757 file_counter,
1758 file_counter == 1 ? _("file") : _("files"),
1759 dir_counter ? ", " : ")\n");
1760 g_string_append(message, tmp);
1761 g_free(tmp);
1764 if (file_counter == 0 && dir_counter == 0)
1765 g_string_append(message, _("no directories)\n"));
1766 else if (dir_counter)
1768 tmp = g_strdup_printf("%ld %s)\n",
1769 dir_counter,
1770 dir_counter == 1 ? _("directory")
1771 : _("directories"));
1772 g_string_append(message, tmp);
1773 g_free(tmp);
1776 send();
1779 #ifdef DO_MOUNT_POINTS
1780 static void mount_cb(gpointer data)
1782 GList *paths = (GList *) data;
1783 gboolean mount_points = FALSE;
1785 for (; paths; paths = paths->next)
1787 guchar *path = (guchar *) paths->data;
1789 if (mount_is_mounted(path))
1790 do_mount(path, FALSE); /* Unmount */
1791 else if (g_hash_table_lookup(fstab_mounts, path))
1792 do_mount(path, TRUE); /* Mount */
1793 else
1794 continue;
1796 mount_points = TRUE;
1799 g_string_sprintf(message,
1800 mount_points ? _("'\nDone\n")
1801 : _("!No mount points selected!\n"));
1802 send();
1804 #endif
1806 static guchar *dirname(guchar *path)
1808 guchar *slash;
1810 slash = strrchr(path, '/');
1811 g_return_val_if_fail(slash != NULL, g_strdup(path));
1813 if (slash != path)
1814 return g_strndup(path, slash - path);
1815 return g_strdup("/");
1818 static void delete_cb(gpointer data)
1820 GList *paths = (GList *) data;
1822 while (paths)
1824 guchar *path = (guchar *) paths->data;
1825 guchar *dir;
1827 dir = dirname(path);
1828 send_dir(dir);
1830 if (do_delete(path, dir))
1832 g_string_sprintf(message, "+%s", dir);
1833 send();
1836 g_free(dir);
1837 paths = paths->next;
1840 g_string_sprintf(message, _("'\nDone\n"));
1841 send();
1844 static void find_cb(gpointer data)
1846 GList *all_paths = (GList *) data;
1847 GList *paths;
1849 while (1)
1851 for (paths = all_paths; paths; paths = paths->next)
1853 guchar *path = (guchar *) paths->data;
1855 send_dir(path);
1857 do_find(path, NULL);
1860 g_string_assign(message, _("?Another search?"));
1861 if (!reply(from_parent, TRUE))
1862 break;
1863 g_string_assign(message, "#");
1864 send();
1867 g_string_sprintf(message, _("'\nDone\n"));
1868 send();
1871 static void chmod_cb(gpointer data)
1873 GList *paths = (GList *) data;
1875 for (; paths; paths = paths->next)
1877 guchar *path = (guchar *) paths->data;
1878 struct stat info;
1880 send_dir(path);
1882 if (mc_stat(path, &info) != 0)
1883 send_error();
1884 else if (S_ISLNK(info.st_mode))
1886 g_string_sprintf(message,
1887 _("!'%s' is a symbolic link\n"),
1888 g_basename(path));
1889 send();
1891 else if (do_chmod(path, NULL))
1893 g_string_sprintf(message, "+%s", path); /* XXX */
1894 send();
1898 g_string_sprintf(message, _("'\nDone\n"));
1899 send();
1902 static void list_cb(gpointer data)
1904 GList *paths = (GList *) data;
1906 while (paths)
1908 send_dir((char *) paths->data);
1910 if (action_do_func((char *) paths->data, action_dest))
1912 g_string_sprintf(message, "+%s", action_dest);
1913 send();
1916 paths = paths->next;
1919 g_string_sprintf(message, _("'\nDone\n"));
1920 send();
1923 static GtkWidget *add_toggle(GUIside *gui_side,
1924 guchar *label, guchar *tip,
1925 guchar *code,
1926 gboolean active)
1928 GtkWidget *check;
1930 check = gtk_check_button_new_with_label(label);
1931 gtk_tooltips_set_tip(tooltips, check, tip, NULL);
1932 gtk_object_set_data(GTK_OBJECT(check), "send-code", code);
1933 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check), active);
1934 gtk_signal_connect(GTK_OBJECT(check), "clicked",
1935 GTK_SIGNAL_FUNC(button_reply), gui_side);
1936 gtk_box_pack_start(GTK_BOX(gui_side->flag_box), check, FALSE, TRUE, 0);
1938 return check;
1942 /* EXTERNAL INTERFACE */
1944 void action_find(GList *paths)
1946 GUIside *gui_side;
1947 GtkWidget *hbox, *label, *scroller;
1948 gchar *titles[2];
1950 titles[0] = _("Name");
1951 titles[1] = _("Directory");
1953 if (!paths)
1955 report_error(_("You need to select some items "
1956 "to search through"));
1957 return;
1960 if (!last_find_string)
1961 last_find_string = g_strdup("'core'");
1963 new_entry_string = last_find_string;
1964 gui_side = start_action(paths, find_cb, FALSE);
1965 if (!gui_side)
1966 return;
1968 gui_side->show_info = TRUE;
1969 gui_side->entry_string_func = set_find_string_colour;
1971 scroller = gtk_scrolled_window_new(NULL, NULL);
1972 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroller),
1973 GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
1974 gtk_box_pack_start(GTK_BOX(gui_side->vbox), scroller, TRUE, TRUE, 4);
1975 gui_side->results = gtk_clist_new_with_titles(
1976 sizeof(titles) / sizeof(*titles), titles);
1977 gtk_clist_column_titles_passive(GTK_CLIST(gui_side->results));
1978 gtk_widget_set_usize(gui_side->results, 100, 100);
1979 gtk_clist_set_column_width(GTK_CLIST(gui_side->results), 0, 100);
1980 gtk_clist_set_selection_mode(GTK_CLIST(gui_side->results),
1981 GTK_SELECTION_SINGLE);
1982 gtk_container_add(GTK_CONTAINER(scroller), gui_side->results);
1983 gtk_box_set_child_packing(GTK_BOX(gui_side->vbox),
1984 gui_side->log_hbox, FALSE, TRUE, 4, GTK_PACK_START);
1985 gtk_signal_connect(GTK_OBJECT(gui_side->results), "select_row",
1986 GTK_SIGNAL_FUNC(select_row_callback), gui_side);
1988 hbox = gtk_hbox_new(FALSE, 0);
1989 label = gtk_label_new(_("Expression:"));
1990 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, TRUE, 4);
1991 gui_side->default_string = &last_find_string;
1992 gui_side->entry = gtk_entry_new();
1993 gtk_widget_set_name(gui_side->entry, "fixed-font");
1994 gtk_entry_set_text(GTK_ENTRY(gui_side->entry), last_find_string);
1995 set_find_string_colour(gui_side->entry, last_find_string);
1996 gtk_editable_select_region(GTK_EDITABLE(gui_side->entry), 0, -1);
1997 gtk_widget_set_sensitive(gui_side->entry, FALSE);
1998 gtk_box_pack_start(GTK_BOX(hbox), gui_side->entry, TRUE, TRUE, 4);
1999 gtk_signal_connect(GTK_OBJECT(gui_side->entry), "changed",
2000 GTK_SIGNAL_FUNC(entry_changed), gui_side);
2001 gtk_box_pack_start(GTK_BOX(gui_side->vbox), hbox, FALSE, TRUE, 4);
2002 gtk_box_pack_start(GTK_BOX(hbox),
2003 new_help_button(show_condition_help, NULL),
2004 FALSE, TRUE, 4);
2006 gtk_window_set_title(GTK_WINDOW(gui_side->window), _("Find"));
2007 gtk_window_set_focus(GTK_WINDOW(gui_side->window), gui_side->entry);
2008 gtk_signal_connect(GTK_OBJECT(gui_side->entry), "activate",
2009 GTK_SIGNAL_FUNC(find_return_pressed), gui_side);
2010 number_of_windows++;
2011 gtk_widget_show_all(gui_side->window);
2014 /* Count disk space used by selected items */
2015 void action_usage(GList *paths)
2017 GUIside *gui_side;
2019 if (!paths)
2021 report_error(_("You need to select some items to count"));
2022 return;
2025 gui_side = start_action(paths, usage_cb, TRUE);
2026 if (!gui_side)
2027 return;
2029 gui_side->show_info = TRUE;
2031 gtk_window_set_title(GTK_WINDOW(gui_side->window), _("Disk Usage"));
2032 number_of_windows++;
2033 gtk_widget_show_all(gui_side->window);
2036 /* Mount/unmount listed items (paths).
2037 * Free the list after this function returns.
2038 * If open_dir is TRUE and the dir is successfully mounted, open it.
2039 * quiet can be -1 for default.
2041 void action_mount(GList *paths, gboolean open_dir, int quiet)
2043 #ifdef DO_MOUNT_POINTS
2044 GUIside *gui_side;
2046 if (quiet == -1)
2047 quiet = option_get_int("action_mount");
2049 mount_open_dir = open_dir;
2050 gui_side = start_action(paths, mount_cb, quiet);
2051 if (!gui_side)
2052 return;
2054 gtk_window_set_title(GTK_WINDOW(gui_side->window),
2055 _("Mount / Unmount"));
2056 number_of_windows++;
2057 gtk_widget_show_all(gui_side->window);
2058 #else
2059 report_error(
2060 _("ROX-Filer does not yet support mount points on your "
2061 "system. Sorry."));
2062 #endif /* DO_MOUNT_POINTS */
2065 /* Deletes all selected items in the window */
2066 void action_delete(GList *paths)
2068 GUIside *gui_side;
2070 if (!remove_pinned_ok(paths))
2071 return;
2073 gui_side = start_action(paths, delete_cb,
2074 option_get_int("action_delete"));
2075 if (!gui_side)
2076 return;
2078 gtk_window_set_title(GTK_WINDOW(gui_side->window), _("Delete"));
2079 add_toggle(gui_side,
2080 _("Force"), _("Don't confirm deletion of non-writeable items"),
2081 "F", option_get_int("action_force"));
2082 add_toggle(gui_side,
2083 _("Brief"), _("Only log directories being deleted"),
2084 "B", option_get_int("action_brief"));
2086 number_of_windows++;
2087 gtk_widget_show_all(gui_side->window);
2090 /* Change the permissions of the selected items */
2091 void action_chmod(GList *paths)
2093 GUIside *gui_side;
2094 GtkWidget *hbox, *label, *combo;
2095 static GList *presets = NULL;
2097 if (!paths)
2099 report_error(_("You need to select the items "
2100 "whose permissions you want to change"));
2101 return;
2104 if (!presets)
2106 presets = g_list_append(presets,
2107 _("a+x (Make executable/searchable)"));
2108 presets = g_list_append(presets,
2109 _("a-x (Make non-executable/non-searchable)"));
2110 presets = g_list_append(presets,
2111 _("u+rw (Give owner read+write)"));
2112 presets = g_list_append(presets,
2113 _("go-rwx (Private - owner access only)"));
2114 presets = g_list_append(presets,
2115 _("go=u-w (Public access, not write)"));
2118 if (!last_chmod_string)
2119 last_chmod_string = g_strdup((guchar *) presets->data);
2120 new_entry_string = last_chmod_string;
2121 gui_side = start_action(paths, chmod_cb, FALSE);
2122 if (!gui_side)
2123 return;
2125 add_toggle(gui_side,
2126 _("Brief"), _("Don't list processed files"),
2127 "B", option_get_int("action_brief"));
2128 add_toggle(gui_side,
2129 _("Recurse"), _("Also change contents of subdirectories"),
2130 "R", option_get_int("action_recurse"));
2132 hbox = gtk_hbox_new(FALSE, 0);
2133 label = gtk_label_new(_("Command:"));
2134 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, TRUE, 4);
2135 gui_side->default_string = &last_chmod_string;
2137 combo = gtk_combo_new();
2138 gtk_combo_disable_activate(GTK_COMBO(combo));
2139 gtk_combo_set_use_arrows_always(GTK_COMBO(combo), TRUE);
2140 gtk_combo_set_popdown_strings(GTK_COMBO(combo), presets);
2142 gui_side->entry = GTK_COMBO(combo)->entry;
2143 gtk_entry_set_text(GTK_ENTRY(gui_side->entry), last_chmod_string);
2144 gtk_editable_select_region(GTK_EDITABLE(gui_side->entry), 0, -1);
2145 gtk_widget_set_sensitive(gui_side->entry, FALSE);
2146 gtk_box_pack_start(GTK_BOX(hbox), combo, TRUE, TRUE, 4);
2147 gtk_signal_connect(GTK_OBJECT(gui_side->entry), "changed",
2148 GTK_SIGNAL_FUNC(entry_changed), gui_side);
2149 gtk_box_pack_start(GTK_BOX(gui_side->vbox), hbox, FALSE, TRUE, 0);
2150 gtk_box_pack_start(GTK_BOX(hbox),
2151 new_help_button(show_chmod_help, NULL),
2152 FALSE, TRUE, 4);
2154 gtk_window_set_focus(GTK_WINDOW(gui_side->window), gui_side->entry);
2155 gtk_window_set_title(GTK_WINDOW(gui_side->window), _("Permissions"));
2156 gtk_signal_connect_object(GTK_OBJECT(gui_side->entry), "activate",
2157 GTK_SIGNAL_FUNC(gtk_button_clicked),
2158 GTK_OBJECT(gui_side->yes));
2160 number_of_windows++;
2161 gtk_widget_show_all(gui_side->window);
2164 /* If leaf is NULL then the copy has the same name as the original.
2165 * quiet can be -1 for default.
2167 void action_copy(GList *paths, char *dest, char *leaf, int quiet)
2169 GUIside *gui_side;
2171 if (quiet == -1)
2172 quiet = option_get_int("action_copy");
2174 action_dest = dest;
2175 action_leaf = leaf;
2176 action_do_func = do_copy;
2177 gui_side = start_action(paths, list_cb, quiet);
2178 if (!gui_side)
2179 return;
2181 gtk_window_set_title(GTK_WINDOW(gui_side->window), _("Copy"));
2182 number_of_windows++;
2183 gtk_widget_show_all(gui_side->window);
2186 /* If leaf is NULL then the file is not renamed.
2187 * quiet can be -1 for default.
2189 void action_move(GList *paths, char *dest, char *leaf, int quiet)
2191 GUIside *gui_side;
2193 if (quiet == -1)
2194 quiet = option_get_int("action_move");
2196 action_dest = dest;
2197 action_leaf = leaf;
2198 action_do_func = do_move;
2199 gui_side = start_action(paths, list_cb, quiet);
2200 if (!gui_side)
2201 return;
2203 gtk_window_set_title(GTK_WINDOW(gui_side->window), _("Move"));
2204 number_of_windows++;
2205 gtk_widget_show_all(gui_side->window);
2208 /* If leaf is NULL then the link will have the same name */
2209 /* XXX: No quiet option here? */
2210 void action_link(GList *paths, char *dest, char *leaf)
2212 GUIside *gui_side;
2214 action_dest = dest;
2215 action_leaf = leaf;
2216 action_do_func = do_link;
2217 gui_side = start_action(paths, list_cb, option_get_int("action_link"));
2218 if (!gui_side)
2219 return;
2221 gtk_window_set_title(GTK_WINDOW(gui_side->window), _("Link"));
2222 number_of_windows++;
2223 gtk_widget_show_all(gui_side->window);
2226 void action_init(void)
2228 option_add_int("action_copy", 1, NULL);
2229 option_add_int("action_move", 1, NULL);
2230 option_add_int("action_link", 1, NULL);
2231 option_add_int("action_delete", 0, NULL);
2232 option_add_int("action_mount", 1, NULL);
2233 option_add_int("action_force", o_force, NULL);
2234 option_add_int("action_brief", o_brief, NULL);
2235 option_add_int("action_recurse", o_recurse, NULL);
2238 #define MAX_ASK 4
2240 /* Check to see if any of the selected items (or their children) are
2241 * on the pinboard or panel. If so, ask for confirmation.
2243 * TRUE if it's OK to lose them.
2245 static gboolean remove_pinned_ok(GList *paths)
2247 GList *ask = NULL, *next;
2248 GString *message;
2249 int i, ask_n = 0;
2250 gboolean retval;
2252 while (paths)
2254 guchar *path = (guchar *) paths->data;
2256 if (icons_require(path))
2258 if (++ask_n > MAX_ASK)
2259 break;
2260 ask = g_list_append(ask, path);
2263 paths = paths->next;
2266 if (!ask)
2267 return TRUE;
2269 if (ask_n > MAX_ASK)
2271 message = g_string_new(_("Deleting items such as "));
2272 ask_n--;
2274 else if (ask_n == 1)
2275 message = g_string_new(_("Deleting the item "));
2276 else
2277 message = g_string_new(_("Deleting the items "));
2279 i = 0;
2280 for (next = ask; next; next = next->next)
2282 guchar *path = (guchar *) next->data;
2283 guchar *leaf;
2285 leaf = strrchr(path, '/');
2286 if (leaf)
2287 leaf++;
2288 else
2289 leaf = path;
2291 g_string_append_c(message, '`');
2292 g_string_append(message, leaf);
2293 g_string_append_c(message, '\'');
2294 i++;
2295 if (i == ask_n - 1 && i > 0)
2296 g_string_append(message, _(" and "));
2297 else if (i < ask_n)
2298 g_string_append(message, ", ");
2301 g_list_free(ask);
2303 if (ask_n == 1)
2304 message = g_string_append(message,
2305 _(" will affect some items on the pinboard "
2306 "or panel - really delete it?"));
2307 else
2309 if (ask_n > MAX_ASK)
2310 message = g_string_append_c(message, ',');
2311 message = g_string_append(message,
2312 _(" will affect some items on the pinboard "
2313 "or panel - really delete them?"));
2316 retval = get_choice(PROJECT, message->str,
2317 2, _("Cancel"), _("OK")) == 1;
2319 g_string_free(message, TRUE);
2321 return retval;
2324 void set_find_string_colour(GtkWidget *widget, guchar *string)
2326 #ifndef GTK2
2327 static GtkStyle *error_style = NULL;
2328 FindCondition *cond;
2330 if (!error_style)
2332 error_style = gtk_style_copy(fixed_style);
2333 error_style->fg[GTK_STATE_NORMAL] = red;
2336 cond = find_compile(string);
2337 gtk_widget_set_style(widget, cond ? fixed_style : error_style);
2339 if (cond)
2340 find_condition_free(cond);
2341 #endif