r3663: If we can't guess a file's type from its name or extended attribute, try
[rox-filer.git] / ROX-Filer / src / action.c
blob7507ed4009bf03165eab95facf4a754bda962016
1 /*
2 * $Id$
4 * ROX-Filer, filer for the ROX desktop project
5 * Copyright (C) 2003, 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 #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>
35 #include <utime.h>
36 #include <stdarg.h>
38 #include "global.h"
40 #include "action.h"
41 #include "abox.h"
42 #include "string.h"
43 #include "support.h"
44 #include "gui_support.h"
45 #include "filer.h"
46 #include "display.h"
47 #include "main.h"
48 #include "options.h"
49 #include "modechange.h"
50 #include "find.h"
51 #include "dir.h"
52 #include "icon.h"
53 #include "mount.h"
54 #include "type.h"
55 #include "xtypes.h"
57 /* Parent->Child messages are one character each:
59 * Y/N Yes/No button clicked
60 * F Force deletion of non-writeable items
61 * Q Quiet toggled
62 * E Entry text changed
63 * W neWer toggled
66 typedef struct _GUIside GUIside;
67 typedef void ActionChild(gpointer data);
68 typedef void ForDirCB(const char *path, const char *dest_path);
70 struct _GUIside
72 ABox *abox; /* The action window widget */
74 int from_child; /* File descriptor */
75 FILE *to_child;
76 int input_tag; /* gdk_input_add() */
77 pid_t child; /* Process ID */
78 int errors; /* Number of errors so far */
79 gboolean show_info; /* For Disk Usage */
81 guchar **default_string; /* Changed when the entry changes */
82 void (*entry_string_func)(GtkWidget *widget,
83 const guchar *string);
85 int abort_attempts;
88 /* These don't need to be in a structure because we fork() before
89 * using them again.
91 static gboolean mount_open_dir = FALSE;
92 static int from_parent = 0;
93 static FILE *to_parent = NULL;
94 static gboolean quiet = FALSE;
95 static GString *message = NULL;
96 static const char *action_dest = NULL;
97 static const char *action_leaf = NULL;
98 static void (*action_do_func)(const char *source, const char *dest);
99 static double size_tally; /* For Disk Usage */
100 static unsigned long dir_counter; /* For Disk Usage */
101 static unsigned long file_counter; /* For Disk Usage */
103 static struct mode_change *mode_change = NULL; /* For Permissions */
104 static FindCondition *find_condition = NULL; /* For Find */
105 static MIME_type *type_change = NULL;
107 /* Only used by child */
108 static gboolean o_force = FALSE;
109 static gboolean o_brief = FALSE;
110 static gboolean o_recurse = FALSE;
111 static gboolean o_newer = FALSE;
113 static Option o_action_copy, o_action_move, o_action_link;
114 static Option o_action_delete, o_action_mount;
115 static Option o_action_force, o_action_brief, o_action_recurse;
116 static Option o_action_newer;
118 /* Whenever the text in these boxes is changed we store a copy of the new
119 * string to be used as the default next time.
121 static guchar *last_chmod_string = NULL;
122 static guchar *last_find_string = NULL;
123 static guchar *last_settype_string = NULL;
125 /* Set to one of the above before forking. This may change over a call to
126 * reply(). It is reset to NULL once the text is parsed.
128 static guchar *new_entry_string = NULL;
130 /* Static prototypes */
131 static void send_done(void);
132 static void send_check_path(const gchar *path);
133 static void send_mount_path(const gchar *path);
134 static gboolean printf_send(const char *msg, ...);
135 static gboolean send_msg(void);
136 static gboolean send_error(void);
137 static gboolean send_dir(const char *dir);
138 static gboolean read_exact(int source, char *buffer, ssize_t len);
139 static void do_mount(const guchar *path, gboolean mount);
140 static gboolean printf_reply(int fd, gboolean ignore_quiet,
141 const char *msg, ...);
142 static gboolean remove_pinned_ok(GList *paths);
144 /* SUPPORT */
147 /* This is called whenever the user edits the entry box (if any) - send the
148 * new string.
150 static void entry_changed(GtkEditable *entry, GUIside *gui_side)
152 guchar *text;
154 g_return_if_fail(gui_side->default_string != NULL);
156 text = gtk_editable_get_chars(entry, 0, -1);
158 if (gui_side->entry_string_func)
159 gui_side->entry_string_func(GTK_WIDGET(entry), text);
161 g_free(*(gui_side->default_string));
162 *(gui_side->default_string) = text; /* Gets text's ref */
164 if (!gui_side->to_child)
165 return;
167 fputc('E', gui_side->to_child);
168 fputs(text, gui_side->to_child);
169 fputc('\n', gui_side->to_child);
170 fflush(gui_side->to_child);
173 void show_condition_help(gpointer data)
175 GtkWidget *help;
176 GtkWidget *text;
178 help = gtk_dialog_new_with_buttons(
179 _("Find expression reference"),
180 NULL, 0,
181 GTK_STOCK_CLOSE, GTK_RESPONSE_CANCEL,
182 NULL);
183 gtk_dialog_set_default_response(GTK_DIALOG(help), GTK_RESPONSE_CANCEL);
185 text = gtk_label_new(NULL);
186 gtk_misc_set_padding(GTK_MISC(text), 2, 2);
187 gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(help)->vbox), text);
188 gtk_label_set_selectable(GTK_LABEL(text), TRUE);
189 gtk_label_set_markup(GTK_LABEL(text), _(
190 "<u>Quick Start</u>\n"
191 "Just put the name of the file you're looking for in single quotes:\n"
192 "<b>'index.html'</b> (to find a file called 'index.html')\n"
193 "\n"
194 "<u>Examples</u>\n"
195 "<b>'*.htm', '*.html'</b> (finds HTML files)\n"
196 "<b>IsDir 'lib'</b> (finds directories called 'lib')\n"
197 "<b>IsReg 'core'</b> (finds a regular file called 'core')\n"
198 "<b>! (IsDir, IsReg)</b> (is neither a directory nor a regular file)\n"
199 "<b>mtime after 1 day ago and size > 1Mb</b> (big, and recently modified)\n"
200 "<b>'CVS' prune, isreg</b> (a regular file not in CVS)\n"
201 "<b>IsReg system(grep -q fred \"%\")</b> (contains the word 'fred')\n"
202 "\n"
203 "<u>Simple Tests</u>\n"
204 "<b>IsReg, IsLink, IsDir, IsChar, IsBlock, IsDev, IsPipe, IsSocket, IsDoor</b> "
205 "(types)\n"
206 "<b>IsSUID, IsSGID, IsSticky, IsReadable, IsWriteable, IsExecutable</b> "
207 "(permissions)\n"
208 "<b>IsEmpty, IsMine</b>\n"
209 "A pattern in single quotes is a shell-style wildcard pattern to match. If it\n"
210 "contains a slash then the match is against the full path; otherwise it is\n"
211 "against the leafname only.\n"
212 "\n"
213 "<u>Comparisons</u>\n"
214 "<b>&lt;, &lt;=, =, !=, &gt;, &gt;=, After, Before</b> (compare two values)\n"
215 "<b>5 bytes, 1Kb, 2Mb, 3Gb</b> (file sizes)\n"
216 "<b>2 secs|mins|hours|days|weeks|years ago|hence</b> (times)\n"
217 "<b>atime, ctime, mtime, now, size, inode, nlinks, uid, gid, blocks</b> "
218 "(values)\n"
219 "\n"
220 "<u>Specials</u>\n"
221 "<b>system(command)</b> (true if 'command' returns with a zero exit status;\n"
222 "a % in 'command' is replaced with the path of the current file)\n"
223 "<b>prune</b> (false, and prevents searching the contents of a directory)."));
225 g_signal_connect(help, "response",
226 G_CALLBACK(gtk_widget_destroy), NULL);
228 gtk_widget_show_all(help);
231 static void show_chmod_help(gpointer data)
233 GtkWidget *help;
234 GtkWidget *text;
236 help = gtk_dialog_new_with_buttons(
237 _("Find expression reference"),
238 NULL, 0,
239 GTK_STOCK_CLOSE, GTK_RESPONSE_CANCEL,
240 NULL);
241 gtk_dialog_set_default_response(GTK_DIALOG(help), GTK_RESPONSE_CANCEL);
243 text = gtk_label_new(NULL);
244 gtk_misc_set_padding(GTK_MISC(text), 2, 2);
245 gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(help)->vbox), text);
246 gtk_label_set_selectable(GTK_LABEL(text), TRUE);
247 gtk_label_set_markup(GTK_LABEL(text), _(
248 "Normally, you can just select a command from the menu (click \n"
249 "on the arrow beside the command box). Sometimes, you need more...\n"
250 "\n"
251 "The format of a command is: <b>CHANGE, CHANGE, ...</b>\n"
252 "Each <b>CHANGE</b> is: <b>WHO HOW PERMISSIONS</b>\n"
253 "<b>WHO</b> is some combination of <b>u</b>, <b>g</b> and <b>o</b> which "
254 "determines whether to\n"
255 "change the permissions for the User (owner), Group or Others.\n"
256 "<b>HOW</b> is <b>+</b>, <b>-</b> or <b>=</b> to add, remove or set "
257 "exactly the permissions.\n"
258 "<b>PERMISSIONS</b> is some combination of the letters <b>rwxXstugo</b>\n"
259 "\n"
260 "Bracketed text and spaces are ignored.\n"
261 "\n"
262 "<u>Examples</u>\n"
263 "<b>u+rw</b>: the file owner gains read and write permission\n"
264 "<b>g=u</b>: the group permissions are set to be the same as the user's\n"
265 "<b>o=u-w</b>: others get the same permissions as the owner, but without "
266 "write permission\n"
267 "<b>a+x</b>: <b>a</b>ll get execute/access permission - same as <b>ugo+x</b>\n"
268 "<b>a+X</b>: directories become accessable by everyone; files which were\n"
269 "executable by anyone become executable by everyone\n"
270 "<b>u+rw, go+r</b>: two commands at once!\n"
271 "<b>u+s</b>: set the SetUID bit - often has no effect on script files\n"
272 "<b>755</b>: set the permissions directly\n"
273 "\n"
274 "See the chmod(1) man page for full details."));
276 g_signal_connect(help, "response",
277 G_CALLBACK(gtk_widget_destroy), NULL);
279 gtk_widget_show_all(help);
283 static void show_settype_help(gpointer data)
285 GtkWidget *help;
286 GtkWidget *text;
288 help = gtk_dialog_new_with_buttons(
289 _("Set type reference"),
290 NULL, 0,
291 GTK_STOCK_CLOSE, GTK_RESPONSE_CANCEL,
292 NULL);
293 gtk_dialog_set_default_response(GTK_DIALOG(help), GTK_RESPONSE_CANCEL);
295 text = gtk_label_new(NULL);
296 gtk_misc_set_padding(GTK_MISC(text), 2, 2);
297 gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(help)->vbox), text);
298 gtk_label_set_selectable(GTK_LABEL(text), TRUE);
299 gtk_label_set_markup(GTK_LABEL(text), _(
300 "Normally ROX-Filer determines the type of a regular file\n"
301 "by matching it's name against a pattern. To change the\n"
302 "type of the file you must rename it.\n"
303 "\n"
304 "Newer file systems can support something called 'Extended\n"
305 "Attributes' which can be used to store additional data with\n"
306 "each file as named parameters. ROX-Filer uses the\n"
307 "'user.mime_type' attribute to store file types.\n"
308 "\n"
309 "File types are only supported for regular files, not\n"
310 "directories, devices, pipes or sockets, and then only\n"
311 "on certain file systems and where the OS implements them.\n"
312 "\n"
313 ATTR_MAN_PAGE));
315 g_signal_connect(help, "response",
316 G_CALLBACK(gtk_widget_destroy), NULL);
318 gtk_widget_show_all(help);
321 static void process_message(GUIside *gui_side, const gchar *buffer)
323 ABox *abox = gui_side->abox;
325 if (*buffer == '?')
326 abox_ask(abox, buffer + 1);
327 else if (*buffer == 's')
328 dir_check_this(buffer + 1); /* Update this item */
329 else if (*buffer == '=')
330 abox_add_filename(abox, buffer + 1);
331 else if (*buffer == '#')
332 abox_clear_results(abox);
333 else if (*buffer == 'X')
335 filer_close_recursive(buffer + 1);
336 /* Let child know it's safe to continue... */
337 fputc('X', gui_side->to_child);
338 fflush(gui_side->to_child);
340 else if (*buffer == 'm' || *buffer == 'M')
342 /* Mount / major changes to this path */
343 if (*buffer == 'M')
345 mount_update(TRUE);
346 mount_user_mount(buffer + 1);
348 filer_check_mounted(buffer + 1);
350 else if (*buffer == '/')
351 abox_set_current_object(abox, buffer + 1);
352 else if (*buffer == 'o')
353 filer_opendir(buffer + 1, NULL, NULL);
354 else if (*buffer == '!')
356 gui_side->errors++;
357 abox_log(abox, buffer + 1, "error");
359 else if (*buffer == '<')
360 abox_set_file(abox, 0, buffer+1);
361 else if (*buffer == '>')
363 abox_set_file(abox, 1, buffer+1);
364 abox_show_compare(abox, TRUE);
366 else
367 abox_log(abox, buffer + 1, NULL);
370 /* Called when the child sends us a message */
371 static void message_from_child(gpointer data,
372 gint source,
373 GdkInputCondition condition)
375 char buf[5];
376 GUIside *gui_side = (GUIside *) data;
377 ABox *abox = gui_side->abox;
378 GtkTextBuffer *text_buffer;
380 text_buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(abox->log));
382 if (read_exact(source, buf, 4))
384 ssize_t message_len;
385 char *buffer;
387 buf[4] = '\0';
388 message_len = strtol(buf, NULL, 16);
389 buffer = g_malloc(message_len + 1);
390 if (message_len > 0 && read_exact(source, buffer, message_len))
392 buffer[message_len] = '\0';
393 process_message(gui_side, buffer);
394 g_free(buffer);
395 return;
397 g_printerr("Child died in the middle of a message.\n");
400 if (gui_side->abort_attempts)
401 abox_log(abox, _("\nProcess terminated.\n"), "error");
403 /* The child is dead */
404 gui_side->child = 0;
406 fclose(gui_side->to_child);
407 gui_side->to_child = NULL;
408 close(gui_side->from_child);
409 g_source_remove(gui_side->input_tag);
410 abox_cancel_ask(gui_side->abox);
412 if (gui_side->errors)
414 guchar *report;
416 if (gui_side->errors == 1)
417 report = g_strdup(_("There was one error.\n"));
418 else
419 report = g_strdup_printf(_("There were %d errors.\n"),
420 gui_side->errors);
422 gtk_text_buffer_insert_at_cursor(text_buffer, report, -1);
424 g_free(report);
426 else if (gui_side->show_info == FALSE)
427 gtk_widget_destroy(GTK_WIDGET(gui_side->abox));
430 /* Scans src_dir, calling cb(item, dest_path) for each item */
431 static void for_dir_contents(ForDirCB *cb,
432 const char *src_dir,
433 const char *dest_path)
435 DIR *d;
436 struct dirent *ent;
437 GList *list = NULL, *next;
439 d = mc_opendir(src_dir);
440 if (!d)
442 /* Message displayed is "ERROR reading 'path': message" */
443 printf_send("!%s '%s': %s\n", _("ERROR reading"),
444 src_dir, g_strerror(errno));
445 return;
448 send_dir(src_dir);
450 while ((ent = mc_readdir(d)))
452 if (ent->d_name[0] == '.' && (ent->d_name[1] == '\0'
453 || (ent->d_name[1] == '.' && ent->d_name[2] == '\0')))
454 continue;
455 list = g_list_prepend(list, g_strdup(make_path(src_dir,
456 ent->d_name)));
458 mc_closedir(d);
460 for (next = list; next; next = next->next)
462 cb((char *) next->data, dest_path);
464 g_free(next->data);
466 g_list_free(list);
469 /* Read this many bytes into the buffer. TRUE on success. */
470 static gboolean read_exact(int source, char *buffer, ssize_t len)
472 while (len > 0)
474 ssize_t got;
475 got = read(source, buffer, len);
476 if (got < 1)
477 return FALSE;
478 len -= got;
479 buffer += got;
481 return TRUE;
484 static void send_done(void)
486 printf_send(_("'\nDone\n"));
489 /* Notify the filer that this item has been updated */
490 static void send_check_path(const gchar *path)
492 printf_send("s%s", path);
495 /* Notify the filer that this whole subtree has changed (eg, been unmounted) */
496 static void send_mount_path(const gchar *path)
498 printf_send("m%s", path);
501 /* Send a message to the filer process. The first character indicates the
502 * type of the message.
504 static gboolean printf_send(const char *msg, ...)
506 va_list args;
507 gchar *tmp;
509 va_start(args, msg);
510 tmp = g_strdup_vprintf(msg, args);
511 va_end(args);
513 g_string_assign(message, tmp);
514 g_free(tmp);
516 return send_msg();
519 /* Send 'message' to our parent process. TRUE on success. */
520 static gboolean send_msg(void)
522 char len_buffer[5];
523 ssize_t len;
525 g_return_val_if_fail(message->len < 0xffff, FALSE);
527 sprintf(len_buffer, "%04x", message->len);
528 fwrite(len_buffer, 1, 4, to_parent);
529 len = fwrite(message->str, 1, message->len, to_parent);
530 fflush(to_parent);
531 return len == (ssize_t) message->len;
534 /* Set the directory indicator at the top of the window */
535 static gboolean send_dir(const char *dir)
537 return printf_send("/%s", dir);
540 static gboolean send_error(void)
542 return printf_send("!%s: %s\n", _("ERROR"), g_strerror(errno));
545 static void response(GtkDialog *dialog, gint response, GUIside *gui_side)
547 gchar code;
549 if (!gui_side->to_child)
550 return;
552 if (response == GTK_RESPONSE_YES)
553 code = 'Y';
554 else if (response == GTK_RESPONSE_NO)
555 code = 'N';
556 else
557 return;
559 fputc(code, gui_side->to_child);
560 fflush(gui_side->to_child);
561 abox_show_compare(gui_side->abox, FALSE);
564 static void flag_toggled(ABox *abox, gint flag, GUIside *gui_side)
566 if (!gui_side->to_child)
567 return;
569 fputc(flag, gui_side->to_child);
570 fflush(gui_side->to_child);
573 static void read_new_entry_text(void)
575 int len;
576 char c;
577 GString *new;
579 new = g_string_new(NULL);
581 for (;;)
583 len = read(from_parent, &c, 1);
584 if (len != 1)
586 fprintf(stderr, "read() error: %s\n",
587 g_strerror(errno));
588 _exit(1); /* Parent died? */
591 if (c == '\n')
592 break;
593 g_string_append_c(new, c);
596 g_free(new_entry_string);
597 new_entry_string = new->str;
598 g_string_free(new, FALSE);
601 static void process_flag(char flag)
603 switch (flag)
605 case 'Q':
606 quiet = !quiet;
607 break;
608 case 'F':
609 o_force = !o_force;
610 break;
611 case 'R':
612 o_recurse = !o_recurse;
613 break;
614 case 'B':
615 o_brief = !o_brief;
616 break;
617 case 'W':
618 o_newer = !o_newer;
619 break;
620 case 'E':
621 read_new_entry_text();
622 break;
623 default:
624 printf_send("!ERROR: Bad message '%c'\n", flag);
625 break;
629 /* If the parent has sent any flag toggles, read them */
630 static void check_flags(void)
632 fd_set set;
633 int got;
634 char retval;
635 struct timeval tv;
637 FD_ZERO(&set);
639 while (1)
641 FD_SET(from_parent, &set);
642 tv.tv_sec = 0;
643 tv.tv_usec = 0;
644 got = select(from_parent + 1, &set, NULL, NULL, &tv);
646 if (got == -1)
647 g_error("select() failed: %s\n", g_strerror(errno));
648 else if (!got)
649 return;
651 got = read(from_parent, &retval, 1);
652 if (got != 1)
653 g_error("read() error: %s\n", g_strerror(errno));
655 process_flag(retval);
659 /* Read until the user sends a reply. If ignore_quiet is TRUE then
660 * the user MUST click Yes or No, else treat quiet on as Yes.
661 * If the user needs prompting then does send_msg().
663 static gboolean printf_reply(int fd, gboolean ignore_quiet,
664 const char *msg, ...)
666 ssize_t len;
667 char retval;
668 va_list args;
669 gchar *tmp;
671 if (quiet && !ignore_quiet)
672 return TRUE;
674 va_start(args, msg);
675 tmp = g_strdup_vprintf(msg, args);
676 va_end(args);
678 g_string_assign(message, tmp);
679 g_free(tmp);
681 send_msg();
683 while (1)
685 len = read(fd, &retval, 1);
686 if (len != 1)
688 fprintf(stderr, "read() error: %s\n",
689 g_strerror(errno));
690 _exit(1); /* Parent died? */
693 switch (retval)
695 case 'Y':
696 printf_send("' %s\n", _("Yes"));
697 return TRUE;
698 case 'N':
699 printf_send("' %s\n", _("No"));
700 return FALSE;
701 default:
702 process_flag(retval);
703 break;
708 static void abort_operation(GtkWidget *widget, gpointer data)
710 GUIside *gui_side = (GUIside *) data;
712 if (gui_side->child)
714 if (gui_side->abort_attempts == 0)
716 abox_log(ABOX(widget),
717 _("\nAsking child process to terminate...\n"),
718 "error");
719 kill(-gui_side->child, SIGTERM);
721 else
723 abox_log(ABOX(widget),
724 _("\nTrying to KILL run-away process...\n"),
725 "error");
726 kill(-gui_side->child, SIGKILL);
727 kill(-gui_side->child, SIGCONT);
729 gui_side->abort_attempts++;
731 else
732 gtk_widget_destroy(widget);
735 static void destroy_action_window(GtkWidget *widget, gpointer data)
737 GUIside *gui_side = (GUIside *) data;
739 if (gui_side->child)
741 kill(-gui_side->child, SIGTERM);
742 fclose(gui_side->to_child);
743 close(gui_side->from_child);
744 g_source_remove(gui_side->input_tag);
747 g_free(gui_side);
749 one_less_window();
752 /* Create two pipes, fork() a child and return a pointer to a GUIside struct
753 * (NULL on failure). The child calls func().
755 static GUIside *start_action(GtkWidget *abox, ActionChild *func, gpointer data,
756 int force, int brief, int recurse, int newer)
758 gboolean autoq;
759 int filedes[4]; /* 0 and 2 are for reading */
760 GUIside *gui_side;
761 pid_t child;
762 struct sigaction act;
764 if (pipe(filedes))
766 report_error("pipe: %s", g_strerror(errno));
767 gtk_widget_destroy(abox);
768 return NULL;
771 if (pipe(filedes + 2))
773 close(filedes[0]);
774 close(filedes[1]);
775 report_error("pipe: %s", g_strerror(errno));
776 gtk_widget_destroy(abox);
777 return NULL;
780 autoq = gtk_toggle_button_get_active(
781 GTK_TOGGLE_BUTTON(ABOX(abox)->quiet));
783 o_force = force;
784 o_brief = brief;
785 o_recurse = recurse;
786 o_newer = newer;
788 child = fork();
789 switch (child)
791 case -1:
792 report_error("fork: %s", g_strerror(errno));
793 gtk_widget_destroy(abox);
794 return NULL;
795 case 0:
796 /* We are the child */
798 /* Create a new process group */
799 setpgid(0, 0);
801 quiet = autoq;
803 dir_drop_all_dnotifies();
805 /* Reset the SIGCHLD handler */
806 act.sa_handler = SIG_DFL;
807 sigemptyset(&act.sa_mask);
808 act.sa_flags = 0;
809 sigaction(SIGCHLD, &act, NULL);
811 message = g_string_new(NULL);
812 close(filedes[0]);
813 close(filedes[3]);
814 to_parent = fdopen(filedes[1], "wb");
815 from_parent = filedes[2];
816 func(data);
817 send_dir("");
818 _exit(0);
821 /* We are the parent */
822 close(filedes[1]);
823 close(filedes[2]);
824 gui_side = g_new(GUIside, 1);
825 gui_side->from_child = filedes[0];
826 gui_side->to_child = fdopen(filedes[3], "wb");
827 gui_side->child = child;
828 gui_side->errors = 0;
829 gui_side->show_info = FALSE;
830 gui_side->default_string = NULL;
831 gui_side->entry_string_func = NULL;
832 gui_side->abort_attempts = 0;
834 gui_side->abox = ABOX(abox);
835 g_signal_connect(abox, "destroy",
836 G_CALLBACK(destroy_action_window), gui_side);
838 g_signal_connect(abox, "response", G_CALLBACK(response), gui_side);
839 g_signal_connect(abox, "flag_toggled",
840 G_CALLBACK(flag_toggled), gui_side);
841 g_signal_connect(abox, "abort_operation",
842 G_CALLBACK(abort_operation), gui_side);
844 gui_side->input_tag = gdk_input_add_full(gui_side->from_child,
845 GDK_INPUT_READ,
846 message_from_child,
847 gui_side, NULL);
849 return gui_side;
852 /* ACTIONS ON ONE ITEM */
854 /* These may call themselves recursively, or ask questions, etc */
856 /* Updates the global size_tally, file_counter and dir_counter */
857 static void do_usage(const char *src_path, const char *unused)
859 struct stat info;
861 check_flags();
863 if (mc_lstat(src_path, &info))
865 printf_send("'%s:\n", src_path);
866 send_error();
868 else if (S_ISREG(info.st_mode) || S_ISLNK(info.st_mode))
870 file_counter++;
871 size_tally += info.st_size;
873 else if (S_ISDIR(info.st_mode))
875 dir_counter++;
876 if (printf_reply(from_parent, FALSE,
877 _("?Count contents of %s?"), src_path))
879 char *safe_path;
880 safe_path = g_strdup(src_path);
881 for_dir_contents(do_usage, safe_path, safe_path);
882 g_free(safe_path);
885 else
886 file_counter++;
889 /* dest_path is the dir containing src_path */
890 static void do_delete(const char *src_path, const char *unused)
892 struct stat info;
893 gboolean write_prot;
894 char *safe_path;
896 check_flags();
898 if (mc_lstat(src_path, &info))
900 send_error();
901 return;
904 write_prot = S_ISLNK(info.st_mode) ? FALSE
905 : access(src_path, W_OK) != 0;
906 if (write_prot || !quiet)
908 printf_send("<%s", src_path);
909 printf_send(">");
910 if (!printf_reply(from_parent, write_prot && !o_force,
911 _("?Delete %s'%s'?"),
912 write_prot ? _("WRITE-PROTECTED ") : "",
913 src_path))
914 return;
916 else if (!o_brief)
917 printf_send(_("'Deleting '%s'\n"), src_path);
919 safe_path = g_strdup(src_path);
921 if (S_ISDIR(info.st_mode))
923 for_dir_contents(do_delete, safe_path, safe_path);
924 if (rmdir(safe_path))
926 g_free(safe_path);
927 send_error();
928 return;
930 printf_send(_("'Directory '%s' deleted\n"), safe_path);
931 send_mount_path(safe_path);
933 else if (unlink(src_path))
934 send_error();
935 else
937 send_check_path(safe_path);
938 if (strcmp(g_basename(safe_path), ".DirIcon") == 0)
940 gchar *dir;
941 dir = g_path_get_dirname(safe_path);
942 send_check_path(dir);
943 g_free(dir);
947 g_free(safe_path);
950 static void do_eject(const char *path)
952 const char *argv[3] = {NULL, NULL, NULL};
953 char *err;
955 check_flags();
957 if (!quiet)
959 printf_send("<%s", path);
960 printf_send(">");
961 if (!printf_reply(from_parent, !o_force,
962 _("?Eject '%s'?"),
963 path))
964 return;
966 else if (!o_brief)
967 printf_send(_("'Eject '%s'\n"), path);
969 /* Need to close all sub-directories now, or we
970 * can't unmount if dnotify is used.
973 char c = '?';
974 printf_send("X%s", path);
975 /* Wait until it's safe... */
976 read(from_parent, &c, 1);
977 g_return_if_fail(c == 'X');
980 argv[0] = "eject";
981 argv[1] = path;
982 argv[2] = NULL;
983 err = fork_exec_wait(argv);
984 if (err)
986 printf_send(_("!%s\neject failed\n"), err);
987 g_free(err);
990 printf_send("M%s", path);
994 /* path is the item to check. If is is a directory then we may recurse
995 * (unless prune is used).
997 static void do_find(const char *path, const char *unused)
999 FindInfo info;
1001 check_flags();
1003 if (!quiet)
1005 if (!printf_reply(from_parent, FALSE, _("?Check '%s'?"), path))
1006 return;
1009 for (;;)
1011 if (new_entry_string)
1013 find_condition_free(find_condition);
1014 find_condition = find_compile(new_entry_string);
1015 null_g_free(&new_entry_string);
1018 if (find_condition)
1019 break;
1021 printf_send(_("!Invalid find condition - "
1022 "change it and try again\n"));
1023 if (!printf_reply(from_parent, TRUE,
1024 _("?Check '%s'?"), path))
1025 return;
1028 if (mc_lstat(path, &info.stats))
1030 send_error();
1031 printf_send(_("'(while checking '%s')\n"), path);
1032 return;
1035 info.fullpath = path;
1036 time(&info.now); /* XXX: Not for each check! */
1038 info.leaf = g_basename(path);
1039 info.prune = FALSE;
1040 if (find_test_condition(find_condition, &info))
1041 printf_send("=%s", path);
1043 if (S_ISDIR(info.stats.st_mode) && !info.prune)
1045 char *safe_path;
1046 safe_path = g_strdup(path);
1047 for_dir_contents(do_find, safe_path, safe_path);
1048 g_free(safe_path);
1052 /* Like mode_compile(), but ignores spaces and bracketed bits */
1053 static struct mode_change *nice_mode_compile(const char *mode_string,
1054 unsigned int masked_ops)
1056 GString *new;
1057 int brackets = 0;
1058 struct mode_change *retval = NULL;
1060 new = g_string_new(NULL);
1062 for (; *mode_string; mode_string++)
1064 if (*mode_string == '(')
1065 brackets++;
1066 if (*mode_string == ')')
1068 brackets--;
1069 if (brackets < 0)
1070 break;
1071 continue;
1074 if (brackets == 0 && *mode_string != ' ')
1075 g_string_append_c(new, *mode_string);
1078 if (brackets == 0)
1079 retval = mode_compile(new->str, masked_ops);
1080 g_string_free(new, TRUE);
1081 return retval;
1084 static void do_chmod(const char *path, const char *unused)
1086 struct stat info;
1087 mode_t new_mode;
1089 check_flags();
1091 if (mc_lstat(path, &info))
1093 send_error();
1094 return;
1096 if (S_ISLNK(info.st_mode))
1097 return;
1099 if (!quiet)
1101 printf_send("<%s", path);
1102 printf_send(">");
1103 if (!printf_reply(from_parent, FALSE,
1104 _("?Change permissions of '%s'?"), path))
1105 return;
1107 else if (!o_brief)
1108 printf_send(_("'Changing permissions of '%s'\n"), path);
1110 for (;;)
1112 if (new_entry_string)
1114 if (mode_change)
1115 mode_free(mode_change);
1116 mode_change = nice_mode_compile(new_entry_string,
1117 MODE_MASK_ALL);
1118 null_g_free(&new_entry_string);
1121 if (mode_change)
1122 break;
1124 printf_send(
1125 _("!Invalid mode command - change it and try again\n"));
1126 if (!printf_reply(from_parent, TRUE,
1127 _("?Change permissions of '%s'?"), path))
1128 return;
1131 if (mc_lstat(path, &info))
1133 send_error();
1134 return;
1136 if (S_ISLNK(info.st_mode))
1137 return;
1139 new_mode = mode_adjust(info.st_mode, mode_change);
1140 if (chmod(path, new_mode))
1142 send_error();
1143 return;
1146 send_check_path(path);
1148 if (S_ISDIR(info.st_mode))
1150 send_mount_path(path);
1152 if (o_recurse)
1154 guchar *safe_path;
1155 safe_path = g_strdup(path);
1156 for_dir_contents(do_chmod, safe_path, safe_path);
1157 g_free(safe_path);
1162 static void do_settype(const char *path, const char *unused)
1164 struct stat info;
1166 check_flags();
1168 if (mc_lstat(path, &info))
1170 send_error();
1171 return;
1173 if (S_ISLNK(info.st_mode))
1174 return;
1176 if (!quiet)
1178 printf_send("<%s", path);
1179 printf_send(">");
1180 if (!printf_reply(from_parent, FALSE,
1181 _("?Change type of '%s'?"), path))
1182 return;
1185 for (;;)
1187 if (new_entry_string)
1189 type_change = mime_type_lookup(new_entry_string);
1190 null_g_free(&new_entry_string);
1193 if (type_change)
1194 break;
1196 printf_send(_("!Invalid type - "
1197 "change it and try again\n"));
1198 if (!printf_reply(from_parent, TRUE,
1199 _("?Change type of '%s'?"), path))
1200 return;
1203 if (mc_lstat(path, &info))
1205 send_error();
1206 return;
1208 if (S_ISLNK(info.st_mode))
1209 return;
1211 if (S_ISREG(info.st_mode))
1213 if (!o_brief)
1215 const char *comment;
1217 comment = mime_type_comment(type_change);
1218 printf_send(_("'Changing type of '%s' to '%s'\n"), path,
1219 comment);
1222 if (xtype_set(path, type_change))
1224 send_error();
1225 return;
1228 send_check_path(path);
1230 else if (S_ISDIR(info.st_mode))
1232 if (o_recurse)
1234 guchar *safe_path;
1235 safe_path = g_strdup(path);
1236 for_dir_contents(do_settype, safe_path, unused);
1237 g_free(safe_path);
1242 /* We want to copy 'object' into directory 'dir'. If 'action_leaf'
1243 * is set then that is the new leafname, otherwise the leafname stays
1244 * the same.
1246 static const char *make_dest_path(const char *object, const char *dir)
1248 const char *leaf;
1250 if (action_leaf)
1251 leaf = action_leaf;
1252 else
1254 leaf = strrchr(object, '/');
1255 if (!leaf)
1256 leaf = object; /* Error? */
1257 else
1258 leaf++;
1261 return make_path(dir, leaf);
1264 /* If action_leaf is not NULL it specifies the new leaf name */
1265 static void do_copy2(const char *path, const char *dest)
1267 const char *dest_path;
1268 struct stat info;
1269 struct stat dest_info;
1271 check_flags();
1273 dest_path = make_dest_path(path, dest);
1275 if (mc_lstat(path, &info))
1277 send_error();
1278 return;
1281 if (mc_lstat(dest_path, &dest_info) == 0)
1283 int err;
1284 gboolean merge;
1286 merge = S_ISDIR(info.st_mode) && S_ISDIR(dest_info.st_mode);
1288 if (!merge && o_newer && info.st_mtime > dest_info.st_mtime)
1290 /* Newer; keep going */
1292 else
1294 printf_send("<%s", path);
1295 printf_send(">%s", dest_path);
1296 if (!printf_reply(from_parent, TRUE,
1297 _("?'%s' already exists - %s?"),
1298 dest_path,
1299 merge ? _("merge contents")
1300 : _("overwrite")))
1301 return;
1304 if (!merge)
1306 if (S_ISDIR(dest_info.st_mode))
1307 err = rmdir(dest_path);
1308 else
1309 err = unlink(dest_path);
1311 if (err)
1313 send_error();
1314 if (errno != ENOENT)
1315 return;
1316 printf_send(_("'Trying copy anyway...\n"));
1320 else if (!quiet)
1322 printf_send("<%s", path);
1323 printf_send(">");
1324 if (!printf_reply(from_parent, FALSE,
1325 _("?Copy %s as %s?"), path, dest_path))
1326 return;
1328 else if (!o_brief || S_ISDIR(info.st_mode))
1329 printf_send(_("'Copying %s as %s\n"), path, dest_path);
1331 if (S_ISDIR(info.st_mode))
1333 mode_t mode = info.st_mode;
1334 char *safe_path, *safe_dest;
1335 struct stat dest_info;
1336 gboolean exists;
1338 safe_path = g_strdup(path);
1339 safe_dest = g_strdup(dest_path);
1341 exists = !mc_lstat(dest_path, &dest_info);
1343 if (exists && !S_ISDIR(dest_info.st_mode))
1344 printf_send(_("!ERROR: Destination already exists, "
1345 "but is not a directory\n"));
1346 else if (exists == FALSE && mkdir(dest_path, 0700 | mode))
1347 send_error();
1348 else
1350 if (!exists)
1351 /* (just been created then) */
1352 send_check_path(dest_path);
1354 action_leaf = NULL;
1355 for_dir_contents(do_copy2, safe_path, safe_dest);
1356 /* Note: dest_path now invalid... */
1358 if (!exists)
1360 struct utimbuf utb;
1362 /* We may have created the directory with
1363 * more permissions than the source so that
1364 * we could write to it... change it back now.
1366 if (chmod(safe_dest, mode))
1368 /* Some filesystems don't support
1369 * SetGID and SetUID bits. Ignore
1370 * these errors.
1372 if (errno != EPERM)
1373 send_error();
1376 /* Also, try to preserve the timestamps */
1377 utb.actime = info.st_atime;
1378 utb.modtime = info.st_mtime;
1380 utime(safe_dest, &utb);
1384 g_free(safe_path);
1385 g_free(safe_dest);
1387 else if (S_ISLNK(info.st_mode))
1389 char *target;
1391 /* Not all versions of cp(1) can make symlinks,
1392 * so we special-case it.
1395 target = readlink_dup(path);
1396 if (target)
1398 if (symlink(target, dest_path))
1399 send_error();
1400 else
1401 send_check_path(dest_path);
1403 g_free(target);
1405 else
1406 send_error();
1408 else
1410 guchar *error;
1412 error = copy_file(path, dest_path);
1414 if (error)
1416 printf_send(_("!%s\nFailed to copy '%s'\n"),
1417 error, path);
1418 g_free(error);
1420 else
1421 send_check_path(dest_path);
1425 /* If action_leaf is not NULL it specifies the new leaf name */
1426 static void do_move2(const char *path, const char *dest)
1428 const char *dest_path;
1429 const char *argv[] = {"mv", "-f", NULL, NULL, NULL};
1430 struct stat info2;
1431 gboolean is_dir;
1432 char *err;
1434 check_flags();
1436 dest_path = make_dest_path(path, dest);
1438 is_dir = mc_lstat(path, &info2) == 0 && S_ISDIR(info2.st_mode);
1440 if (access(dest_path, F_OK) == 0)
1442 struct stat info;
1443 int err;
1445 if (mc_lstat(dest_path, &info))
1447 send_error();
1448 return;
1451 if (!is_dir && o_newer && info2.st_mtime > info.st_mtime)
1453 /* Newer; keep going */
1455 else
1457 printf_send("<%s", path);
1458 printf_send(">%s", dest_path);
1459 if (!printf_reply(from_parent, TRUE,
1460 _("?'%s' already exists - overwrite?"),
1461 dest_path))
1462 return;
1465 if (S_ISDIR(info.st_mode))
1466 err = rmdir(dest_path);
1467 else
1468 err = unlink(dest_path);
1470 if (err)
1472 send_error();
1473 if (errno != ENOENT)
1474 return;
1475 printf_send(_("'Trying move anyway...\n"));
1478 else if (!quiet)
1480 printf_send("<%s", path);
1481 printf_send(">");
1482 if (!printf_reply(from_parent, FALSE,
1483 _("?Move %s as %s?"), path, dest_path))
1484 return;
1486 else if (!o_brief)
1487 printf_send(_("'Moving %s as %s\n"), path, dest_path);
1489 argv[2] = path;
1490 argv[3] = dest_path;
1492 err = fork_exec_wait(argv);
1493 if (err)
1495 printf_send(_("!%s\nFailed to move %s as %s\n"),
1496 err, path, dest_path);
1497 g_free(err);
1499 else
1501 send_check_path(dest_path);
1503 if (is_dir)
1504 send_mount_path(path);
1505 else
1506 send_check_path(path);
1510 /* Copy path to dest.
1511 * Check that path not copied into itself.
1513 static void do_copy(const char *path, const char *dest)
1515 if (is_sub_dir(make_dest_path(path, dest), path))
1516 printf_send(_("!ERROR: Can't copy object into itself\n"));
1517 else
1519 do_copy2(path, dest);
1520 send_check_path(dest);
1524 /* Move path to dest.
1525 * Check that path not moved into itself.
1527 static void do_move(const char *path, const char *dest)
1529 if (is_sub_dir(make_dest_path(path, dest), path))
1530 printf_send(
1531 _("!ERROR: Can't move/rename object into itself\n"));
1532 else
1534 do_move2(path, dest);
1535 send_check_path(dest);
1539 static void do_link(const char *path, const char *dest)
1541 const char *dest_path;
1543 check_flags();
1545 dest_path = make_dest_path(path, dest);
1547 if (quiet)
1548 printf_send(_("'Linking %s as %s\n"), path, dest_path);
1549 else {
1550 printf_send("<%s", path);
1551 printf_send(">");
1552 if (!printf_reply(from_parent, FALSE,
1553 _("?Link %s as %s?"), path, dest_path))
1554 return;
1557 if (symlink(path, dest_path))
1558 send_error();
1559 else
1560 send_check_path(dest_path);
1563 /* Mount/umount this item (depending on 'mount') */
1564 static void do_mount(const guchar *path, gboolean mount)
1566 const char *argv[3] = {NULL, NULL, NULL};
1567 char *err;
1569 check_flags();
1571 argv[0] = mount ? "mount" : "umount";
1572 argv[1] = path;
1574 if (quiet)
1575 printf_send(mount ? _("'Mounting %s\n")
1576 : _("'Unmounting %s\n"),
1577 path);
1578 else if (!printf_reply(from_parent, FALSE,
1579 mount ? _("?Mount %s?")
1580 : _("?Unmount %s?"),
1581 path))
1582 return;
1584 if (!mount)
1586 char c = '?';
1587 /* Need to close all sub-directories now, or we
1588 * can't unmount if dnotify is used.
1590 printf_send("X%s", path);
1591 /* Wait until it's safe... */
1592 read(from_parent, &c, 1);
1593 g_return_if_fail(c == 'X');
1596 err = fork_exec_wait(argv);
1597 if (err)
1599 printf_send(mount ?
1600 _("!%s\nMount failed\n") :
1601 _("!%s\nUnmount failed\n"), err);
1602 g_free(err);
1604 /* Mount may have worked even on error, eg if we try to mount
1605 * a read-only disk read/write, it gets mounted read-only
1606 * with an error.
1608 if (mount && mount_is_mounted(path, NULL, NULL))
1609 printf_send(_("'(seems to be mounted now anyway)\n"));
1610 else
1611 return;
1614 printf_send("M%s", path);
1615 if (mount && mount_open_dir)
1616 printf_send("o%s", path);
1619 /* CHILD MAIN LOOPS */
1621 /* After forking, the child calls one of these functions */
1623 /* We use a double for total size in order to count beyond 4Gb */
1624 static void usage_cb(gpointer data)
1626 GList *paths = (GList *) data;
1627 double total_size = 0;
1629 dir_counter = file_counter = 0;
1631 for (; paths; paths = paths->next)
1633 guchar *path = (guchar *) paths->data;
1635 send_dir(path);
1637 size_tally = 0;
1639 do_usage(path, NULL);
1641 printf_send("'%s: %s\n",
1642 g_basename(path),
1643 format_double_size(size_tally));
1644 total_size += size_tally;
1647 g_string_printf(message, _("'\nTotal: %s ("),
1648 format_double_size(total_size));
1650 if (file_counter)
1651 g_string_append_printf(message,
1652 "%ld %s%s", file_counter,
1653 file_counter == 1 ? _("file") : _("files"),
1654 dir_counter ? ", " : ")\n");
1656 if (file_counter == 0 && dir_counter == 0)
1657 g_string_append(message, _("no directories)\n"));
1658 else if (dir_counter)
1659 g_string_append_printf(message,
1660 "%ld %s)\n", dir_counter,
1661 dir_counter == 1 ? _("directory")
1662 : _("directories"));
1664 send_msg();
1667 #ifdef DO_MOUNT_POINTS
1668 static void mount_cb(gpointer data)
1670 GList *paths = (GList *) data;
1671 gboolean mount_points = FALSE;
1673 for (; paths; paths = paths->next)
1675 guchar *path = (guchar *) paths->data;
1676 guchar *target;
1678 target = readlink_dup(path);
1679 if (!target)
1680 target = path;
1682 if (mount_is_mounted(target, NULL, NULL))
1684 mount_points = TRUE;
1685 do_mount(target, FALSE); /* Unmount */
1687 else if (g_hash_table_lookup(fstab_mounts, target))
1689 mount_points = TRUE;
1690 do_mount(target, TRUE); /* Mount */
1693 if (target != path)
1694 g_free(target);
1697 if (mount_points)
1698 send_done();
1699 else
1700 printf_send(_("!No mount points selected!\n"));
1702 #endif
1704 /* (use g_dirname() instead?) */
1705 static guchar *dirname(guchar *path)
1707 guchar *slash;
1709 slash = strrchr(path, '/');
1710 g_return_val_if_fail(slash != NULL, g_strdup(path));
1712 if (slash != path)
1713 return g_strndup(path, slash - path);
1714 return g_strdup("/");
1717 static void delete_cb(gpointer data)
1719 GList *paths = (GList *) data;
1721 for (; paths; paths = paths->next)
1723 guchar *path = (guchar *) paths->data;
1724 guchar *dir;
1726 dir = dirname(path);
1727 send_dir(dir);
1729 do_delete(path, dir);
1731 g_free(dir);
1734 send_done();
1737 static void eject_cb(gpointer data)
1739 GList *paths = (GList *) data;
1741 for (; paths; paths = paths->next)
1743 guchar *path = (guchar *) paths->data;
1745 send_dir(path);
1747 do_eject(path);
1750 send_done();
1753 static void find_cb(gpointer data)
1755 GList *all_paths = (GList *) data;
1756 GList *paths;
1758 while (1)
1760 for (paths = all_paths; paths; paths = paths->next)
1762 guchar *path = (guchar *) paths->data;
1764 send_dir(path);
1766 do_find(path, NULL);
1769 if (!printf_reply(from_parent, TRUE,
1770 _("?Another search?")))
1771 break;
1772 printf_send("#");
1775 send_done();
1778 static void chmod_cb(gpointer data)
1780 GList *paths = (GList *) data;
1782 for (; paths; paths = paths->next)
1784 guchar *path = (guchar *) paths->data;
1785 struct stat info;
1787 send_dir(path);
1789 if (mc_stat(path, &info) != 0)
1790 send_error();
1791 else if (S_ISLNK(info.st_mode))
1792 printf_send(_("!'%s' is a symbolic link\n"),
1793 g_basename(path));
1794 else
1795 do_chmod(path, NULL);
1798 send_done();
1801 static void settype_cb(gpointer data)
1803 GList *paths = (GList *) data;
1805 for (; paths; paths = paths->next)
1807 guchar *path = (guchar *) paths->data;
1808 struct stat info;
1810 send_dir(path);
1812 if (mc_stat(path, &info) != 0)
1813 send_error();
1814 else if (S_ISLNK(info.st_mode))
1815 printf_send(_("!'%s' is a symbolic link\n"),
1816 g_basename(path));
1817 else
1818 do_settype(path, NULL);
1821 send_done();
1824 static void list_cb(gpointer data)
1826 GList *paths = (GList *) data;
1828 for (; paths; paths = paths->next)
1830 send_dir((char *) paths->data);
1832 action_do_func((char *) paths->data, action_dest);
1835 send_done();
1838 /* EXTERNAL INTERFACE */
1840 void action_find(GList *paths)
1842 GUIside *gui_side;
1843 GtkWidget *abox;
1845 if (!paths)
1847 report_error(_("You need to select some items "
1848 "to search through"));
1849 return;
1852 if (!last_find_string)
1853 last_find_string = g_strdup("'core'");
1855 new_entry_string = last_find_string;
1857 abox = abox_new(_("Find"), FALSE);
1858 gui_side = start_action(abox, find_cb, paths,
1859 o_action_force.int_value,
1860 o_action_brief.int_value,
1861 o_action_recurse.int_value,
1862 o_action_newer.int_value);
1863 if (!gui_side)
1864 return;
1866 abox_add_results(ABOX(abox));
1868 gui_side->default_string = &last_find_string;
1869 abox_add_entry(ABOX(abox), last_find_string,
1870 new_help_button(show_condition_help, NULL));
1871 g_signal_connect(ABOX(abox)->entry, "changed",
1872 G_CALLBACK(entry_changed), gui_side);
1873 set_find_string_colour(ABOX(abox)->entry, last_find_string);
1875 gui_side->show_info = TRUE;
1876 gui_side->entry_string_func = set_find_string_colour;
1878 number_of_windows++;
1879 gtk_widget_show_all(abox);
1882 /* Count disk space used by selected items */
1883 void action_usage(GList *paths)
1885 GUIside *gui_side;
1886 GtkWidget *abox;
1888 if (!paths)
1890 report_error(_("You need to select some items to count"));
1891 return;
1894 abox = abox_new(_("Disk Usage"), TRUE);
1896 gui_side = start_action(abox, usage_cb, paths,
1897 o_action_force.int_value,
1898 o_action_brief.int_value,
1899 o_action_recurse.int_value,
1900 o_action_newer.int_value);
1901 if (!gui_side)
1902 return;
1904 gui_side->show_info = TRUE;
1906 number_of_windows++;
1908 gtk_widget_show(abox);
1911 /* Mount/unmount listed items (paths).
1912 * Free the list after this function returns.
1913 * If open_dir is TRUE and the dir is successfully mounted, open it.
1914 * quiet can be -1 for default.
1916 void action_mount(GList *paths, gboolean open_dir, int quiet)
1918 #ifdef DO_MOUNT_POINTS
1919 GUIside *gui_side;
1920 GtkWidget *abox;
1922 if (quiet == -1)
1923 quiet = o_action_mount.int_value;
1925 mount_open_dir = open_dir;
1927 abox = abox_new(_("Mount / Unmount"), quiet);
1928 gui_side = start_action(abox, mount_cb, paths,
1929 o_action_force.int_value,
1930 o_action_brief.int_value,
1931 o_action_recurse.int_value,
1932 o_action_newer.int_value);
1933 if (!gui_side)
1934 return;
1936 number_of_windows++;
1937 gtk_widget_show(abox);
1938 #else
1939 report_error(
1940 _("ROX-Filer does not yet support mount points on your "
1941 "system. Sorry."));
1942 #endif /* DO_MOUNT_POINTS */
1945 /* Delete these paths */
1946 void action_delete(GList *paths)
1948 GUIside *gui_side;
1949 GtkWidget *abox;
1951 if (!remove_pinned_ok(paths))
1952 return;
1954 abox = abox_new(_("Delete"), o_action_delete.int_value);
1955 gui_side = start_action(abox, delete_cb, paths,
1956 o_action_force.int_value,
1957 o_action_brief.int_value,
1958 o_action_recurse.int_value,
1959 o_action_newer.int_value);
1960 if (!gui_side)
1961 return;
1963 abox_add_flag(ABOX(abox),
1964 _("Force"), _("Don't confirm deletion of non-writeable items"),
1965 'F', o_action_force.int_value);
1966 abox_add_flag(ABOX(abox),
1967 _("Brief"), _("Only log directories being deleted"),
1968 'B', o_action_brief.int_value);
1970 number_of_windows++;
1971 gtk_widget_show(abox);
1974 /* Change the permissions of the selected items */
1975 void action_chmod(GList *paths, gboolean force_recurse, const char *action)
1977 GtkWidget *abox;
1978 GUIside *gui_side;
1979 static GList *presets = NULL;
1980 gboolean recurse = force_recurse || o_action_recurse.int_value;
1982 if (!paths)
1984 report_error(_("You need to select the items "
1985 "whose permissions you want to change"));
1986 return;
1989 if (!presets)
1991 presets = g_list_append(presets, (gchar *)
1992 _("a+x (Make executable/searchable)"));
1993 presets = g_list_append(presets, (gchar *)
1994 _("a-x (Make non-executable/non-searchable)"));
1995 presets = g_list_append(presets, (gchar *)
1996 _("u+rw (Give owner read+write)"));
1997 presets = g_list_append(presets, (gchar *)
1998 _("go-rwx (Private - owner access only)"));
1999 presets = g_list_append(presets, (gchar *)
2000 _("go=u-w (Public access, not write)"));
2003 if (!last_chmod_string)
2004 last_chmod_string = g_strdup((guchar *) presets->data);
2006 if (action)
2007 new_entry_string = g_strdup(action);
2008 else
2009 new_entry_string = g_strdup(last_chmod_string);
2011 abox = abox_new(_("Permissions"), FALSE);
2012 gui_side = start_action(abox, chmod_cb, paths,
2013 o_action_force.int_value,
2014 o_action_brief.int_value,
2015 recurse,
2016 o_action_newer.int_value);
2018 if (!gui_side)
2019 goto out;
2021 abox_add_flag(ABOX(abox),
2022 _("Brief"), _("Don't list processed files"),
2023 'B', o_action_brief.int_value);
2024 abox_add_flag(ABOX(abox),
2025 _("Recurse"), _("Also change contents of subdirectories"),
2026 'R', recurse);
2028 gui_side->default_string = &last_chmod_string;
2029 abox_add_combo(ABOX(abox), _("Command:"), presets, new_entry_string,
2030 new_help_button(show_chmod_help, NULL));
2032 g_signal_connect(ABOX(abox)->entry, "changed",
2033 G_CALLBACK(entry_changed), gui_side);
2034 #if 0
2035 g_signal_connect_swapped(gui_side->entry, "activate",
2036 G_CALLBACK(gtk_button_clicked),
2037 gui_side->yes);
2038 #endif
2040 number_of_windows++;
2041 gtk_widget_show(abox);
2043 out:
2044 null_g_free(&new_entry_string);
2047 /* Set the MIME type of the selected items */
2048 void action_settype(GList *paths, gboolean force_recurse, const char *oldtype)
2050 GtkWidget *abox;
2051 GUIside *gui_side;
2052 GList *presets = NULL;
2053 gboolean recurse = force_recurse || o_action_recurse.int_value;
2055 if (!paths)
2057 report_error(_("You need to select the items "
2058 "whose type you want to change"));
2059 return;
2062 if (!last_settype_string)
2063 last_settype_string = g_strdup("text/plain");
2065 if (oldtype)
2066 new_entry_string = g_strdup(oldtype);
2067 else
2068 new_entry_string = g_strdup(last_settype_string);
2070 abox = abox_new(_("Set type"), FALSE);
2071 gui_side = start_action(abox, settype_cb, paths,
2072 o_action_force.int_value,
2073 o_action_brief.int_value,
2074 recurse,
2075 o_action_newer.int_value);
2077 if (!gui_side)
2078 goto out;
2080 abox_add_flag(ABOX(abox),
2081 _("Brief"), _("Don't list processed files"),
2082 'B', o_action_brief.int_value);
2083 abox_add_flag(ABOX(abox),
2084 _("Recurse"), _("Change contents of subdirectories"),
2085 'R', recurse);
2087 gui_side->default_string = &last_settype_string;
2089 /* Note: get the list again each time -- it can change */
2090 presets = mime_type_name_list();
2091 abox_add_combo(ABOX(abox), _("Type:"), presets, new_entry_string,
2092 new_help_button(show_settype_help, NULL));
2093 g_list_free(presets);
2095 g_signal_connect(ABOX(abox)->entry, "changed",
2096 G_CALLBACK(entry_changed), gui_side);
2098 number_of_windows++;
2099 gtk_widget_show(abox);
2101 out:
2102 null_g_free(&new_entry_string);
2105 /* If leaf is NULL then the copy has the same name as the original.
2106 * quiet can be -1 for default.
2108 void action_copy(GList *paths, const char *dest, const char *leaf, int quiet)
2110 GUIside *gui_side;
2111 GtkWidget *abox;
2113 if (quiet == -1)
2114 quiet = o_action_copy.int_value;
2116 action_dest = dest;
2117 action_leaf = leaf;
2118 action_do_func = do_copy;
2120 abox = abox_new(_("Copy"), quiet);
2121 gui_side = start_action(abox, list_cb, paths,
2122 o_action_force.int_value,
2123 o_action_brief.int_value,
2124 o_action_recurse.int_value,
2125 o_action_newer.int_value);
2126 if (!gui_side)
2127 return;
2129 abox_add_flag(ABOX(abox),
2130 _("Newer"),
2131 _("Only over-write if source is newer than destination."),
2132 'W', o_action_newer.int_value);
2133 abox_add_flag(ABOX(abox),
2134 _("Brief"), _("Only log directories as they are copied"),
2135 'B', o_action_brief.int_value);
2137 number_of_windows++;
2138 gtk_widget_show(abox);
2141 /* If leaf is NULL then the file is not renamed.
2142 * quiet can be -1 for default.
2144 void action_move(GList *paths, const char *dest, const char *leaf, int quiet)
2146 GUIside *gui_side;
2147 GtkWidget *abox;
2149 if (quiet == -1)
2150 quiet = o_action_move.int_value;
2152 action_dest = dest;
2153 action_leaf = leaf;
2154 action_do_func = do_move;
2156 abox = abox_new(_("Move"), quiet);
2157 gui_side = start_action(abox, list_cb, paths,
2158 o_action_force.int_value,
2159 o_action_brief.int_value,
2160 o_action_recurse.int_value,
2161 o_action_newer.int_value);
2162 if (!gui_side)
2163 return;
2165 abox_add_flag(ABOX(abox),
2166 _("Newer"),
2167 _("Only over-write if source is newer than destination."),
2168 'W', o_action_newer.int_value);
2169 abox_add_flag(ABOX(abox),
2170 _("Brief"), _("Don't log each file as it is moved"),
2171 'B', o_action_brief.int_value);
2172 number_of_windows++;
2173 gtk_widget_show(abox);
2176 /* If leaf is NULL then the link will have the same name */
2177 /* XXX: No quiet option here? */
2178 void action_link(GList *paths, const char *dest, const char *leaf)
2180 GtkWidget *abox;
2181 GUIside *gui_side;
2183 action_dest = dest;
2184 action_leaf = leaf;
2185 action_do_func = do_link;
2187 abox = abox_new(_("Link"), o_action_link.int_value);
2188 gui_side = start_action(abox, list_cb, paths,
2189 o_action_force.int_value,
2190 o_action_brief.int_value,
2191 o_action_recurse.int_value,
2192 o_action_newer.int_value);
2193 if (!gui_side)
2194 return;
2196 number_of_windows++;
2197 gtk_widget_show(abox);
2200 /* Eject these paths */
2201 void action_eject(GList *paths)
2203 GUIside *gui_side;
2204 GtkWidget *abox;
2206 abox = abox_new(_("Eject"), TRUE);
2207 gui_side = start_action(abox, eject_cb, paths,
2208 o_action_force.int_value,
2209 o_action_brief.int_value,
2210 o_action_recurse.int_value,
2211 o_action_newer.int_value);
2212 if (!gui_side)
2213 return;
2215 number_of_windows++;
2216 gtk_widget_show(abox);
2219 void action_init(void)
2221 option_add_int(&o_action_copy, "action_copy", 1);
2222 option_add_int(&o_action_move, "action_move", 1);
2223 option_add_int(&o_action_link, "action_link", 1);
2224 option_add_int(&o_action_delete, "action_delete", 0);
2225 option_add_int(&o_action_mount, "action_mount", 1);
2226 option_add_int(&o_action_force, "action_force", FALSE);
2227 option_add_int(&o_action_brief, "action_brief", FALSE);
2228 option_add_int(&o_action_recurse, "action_recurse", FALSE);
2229 option_add_int(&o_action_newer, "action_newer", FALSE);
2232 #define MAX_ASK 4
2234 /* Check to see if any of the selected items (or their children) are
2235 * on the pinboard or panel. If so, ask for confirmation.
2237 * TRUE if it's OK to lose them.
2239 static gboolean remove_pinned_ok(GList *paths)
2241 GList *ask = NULL, *next;
2242 GString *message;
2243 int i, ask_n = 0;
2244 gboolean retval;
2246 for (; paths; paths = paths->next)
2248 guchar *path = (guchar *) paths->data;
2250 if (icons_require(path))
2252 if (++ask_n > MAX_ASK)
2253 break;
2254 ask = g_list_append(ask, path);
2258 if (!ask)
2259 return TRUE;
2261 if (ask_n > MAX_ASK)
2263 message = g_string_new(_("Deleting items such as "));
2264 ask_n--;
2266 else if (ask_n == 1)
2267 message = g_string_new(_("Deleting the item "));
2268 else
2269 message = g_string_new(_("Deleting the items "));
2271 i = 0;
2272 for (next = ask; next; next = next->next)
2274 guchar *path = (guchar *) next->data;
2275 guchar *leaf;
2277 leaf = strrchr(path, '/');
2278 if (leaf)
2279 leaf++;
2280 else
2281 leaf = path;
2283 g_string_append_c(message, '`');
2284 g_string_append(message, leaf);
2285 g_string_append_c(message, '\'');
2286 i++;
2287 if (i == ask_n - 1 && i > 0)
2288 g_string_append(message, _(" and "));
2289 else if (i < ask_n)
2290 g_string_append(message, ", ");
2293 g_list_free(ask);
2295 if (ask_n == 1)
2296 message = g_string_append(message,
2297 _(" will affect some items on the pinboard "
2298 "or panel - really delete it?"));
2299 else
2301 if (ask_n > MAX_ASK)
2302 message = g_string_append_c(message, ',');
2303 message = g_string_append(message,
2304 _(" will affect some items on the pinboard "
2305 "or panel - really delete them?"));
2308 retval = confirm(message->str, GTK_STOCK_DELETE, NULL);
2310 g_string_free(message, TRUE);
2312 return retval;
2315 void set_find_string_colour(GtkWidget *widget, const guchar *string)
2317 FindCondition *cond;
2319 cond = find_compile(string);
2320 entry_set_error(widget, !cond);
2322 find_condition_free(cond);