1 /*
2 * ROX-Filer, filer for the ROX desktop project
3 * Copyright (C) 2006, Thomas Leonard and others (see changelog for details).
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the Free
7 * Software Foundation; either version 2 of the License, or (at your option)
8 * any later version.
9 *
10 * This program is distributed in the hope that it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
13 * more details.
14 *
15 * You should have received a copy of the GNU General Public License along with
16 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
17 * Place, Suite 330, Boston, MA 02111-1307 USA
18 */
19
20 /* action.c - code for handling the filer action windows.
21 * These routines generally fork() and talk to us via pipes.
22 */
23
24 #include "config.h"
25
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <ctype.h>
29 #include <sys/param.h>
30 #include <errno.h>
31 #include <signal.h>
32 #include <sys/time.h>
33 #include <utime.h>
34 #include <stdarg.h>
35
36 #include "global.h"
37
38 #include "action.h"
39 #include "abox.h"
40 #include "string.h"
41 #include "support.h"
42 #include "gui_support.h"
43 #include "filer.h"
44 #include "display.h"
45 #include "main.h"
46 #include "options.h"
47 #include "modechange.h"
48 #include "find.h"
49 #include "dir.h"
50 #include "icon.h"
51 #include "mount.h"
52 #include "type.h"
53 #include "xtypes.h"
54 #include "log.h"
55
56 #if defined(HAVE_GETXATTR)
57 # define ATTR_MAN_PAGE N_("See the attr(5) man page for full details.")
58 #elif defined(HAVE_ATTROPEN)
59 # define ATTR_MAN_PAGE N_("See the fsattr(5) man page for full details.")
60 #else
61 # define ATTR_MAN_PAGE N_("You do not appear to have OS support.")
62 #endif
63
64 /* Parent->Child messages are one character each:
65 *
66 * Y/N Yes/No button clicked
67 * F Force deletion of non-writeable items
68 * Q Quiet toggled
69 * E Entry text changed
70 * W neWer toggled
71 */
72
73 typedef struct _GUIside GUIside;
74 typedef void ActionChild(gpointer data);
75 typedef void ForDirCB(const char *path, const char *dest_path);
76
77 struct _GUIside
78 {
79 ABox *abox; /* The action window widget */
80
81 int from_child; /* File descriptor */
82 FILE *to_child;
83 int input_tag; /* gdk_input_add() */
84 pid_t child; /* Process ID */
85 int errors; /* Number of errors so far */
86 gboolean show_info; /* For Disk Usage */
87
88 guchar **default_string; /* Changed when the entry changes */
89 void (*entry_string_func)(GtkWidget *widget,
90 const guchar *string);
91
92 int abort_attempts;
93 };
94
95 /* These don't need to be in a structure because we fork() before
96 * using them again.
97 */
98 static gboolean mount_open_dir = FALSE;
99 static gboolean mount_mount = FALSE; /* (FALSE => unmount) */
100 static int from_parent = 0;
101 static FILE *to_parent = NULL;
102 static gboolean quiet = FALSE;
103 static GString *message = NULL;
104 static const char *action_dest = NULL;
105 static const char *action_leaf = NULL;
106 static void (*action_do_func)(const char *source, const char *dest);
107 static double size_tally; /* For Disk Usage */
108 static unsigned long dir_counter; /* For Disk Usage */
109 static unsigned long file_counter; /* For Disk Usage */
110
111 static struct mode_change *mode_change = NULL; /* For Permissions */
112 static FindCondition *find_condition = NULL; /* For Find */
113 static MIME_type *type_change = NULL;
114
115 /* Only used by child */
116 static gboolean o_force = FALSE;
117 static gboolean o_brief = FALSE;
118 static gboolean o_recurse = FALSE;
119 static gboolean o_newer = FALSE;
120
121 static Option o_action_copy, o_action_move, o_action_link;
122 static Option o_action_delete, o_action_mount;
123 static Option o_action_force, o_action_brief, o_action_recurse;
124 static Option o_action_newer;
125
126 static Option o_action_mount_command;
127 static Option o_action_umount_command;
128 static Option o_action_eject_command;
129
130 /* Whenever the text in these boxes is changed we store a copy of the new
131 * string to be used as the default next time.
132 */
133 static guchar *last_chmod_string = NULL;
134 static guchar *last_find_string = NULL;
135 static guchar *last_settype_string = NULL;
136
137 /* Set to one of the above before forking. This may change over a call to
138 * reply(). It is reset to NULL once the text is parsed.
139 */
140 static guchar *new_entry_string = NULL;
141
142 /* Static prototypes */
143 static void send_done(void);
144 static void send_check_path(const gchar *path);
145 static void send_mount_path(const gchar *path);
146 static gboolean printf_send(const char *msg, ...);
147 static gboolean send_msg(void);
148 static gboolean send_error(void);
149 static gboolean send_dir(const char *dir);
150 static gboolean read_exact(int source, char *buffer, ssize_t len);
151 static void do_mount(const guchar *path, gboolean mount);
152 static gboolean printf_reply(int fd, gboolean ignore_quiet,
153 const char *msg, ...);
154 static gboolean remove_pinned_ok(GList *paths);
155
156 /* SUPPORT */
157
158
159 /* This is called whenever the user edits the entry box (if any) - send the
160 * new string.
161 */
entry_changed(GtkEditable * entry,GUIside * gui_side)162 static void entry_changed(GtkEditable *entry, GUIside *gui_side)
163 {
164 guchar *text;
165
166 g_return_if_fail(gui_side->default_string != NULL);
167
168 text = gtk_editable_get_chars(entry, 0, -1);
169
170 if (gui_side->entry_string_func)
171 gui_side->entry_string_func(GTK_WIDGET(entry), text);
172
173 g_free(*(gui_side->default_string));
174 *(gui_side->default_string) = text; /* Gets text's ref */
175
176 if (!gui_side->to_child)
177 return;
178
179 fputc('E', gui_side->to_child);
180 fputs(text, gui_side->to_child);
181 fputc('\n', gui_side->to_child);
182 fflush(gui_side->to_child);
183 }
184
show_condition_help(gpointer data)185 void show_condition_help(gpointer data)
186 {
187 GtkWidget *help;
188 GtkWidget *text;
189
190 help = gtk_dialog_new_with_buttons(
191 _("Find expression reference"),
192 NULL, 0,
193 GTK_STOCK_CLOSE, GTK_RESPONSE_CANCEL,
194 NULL);
195 gtk_dialog_set_default_response(GTK_DIALOG(help), GTK_RESPONSE_CANCEL);
196
197 text = gtk_label_new(NULL);
198 gtk_misc_set_padding(GTK_MISC(text), 2, 2);
199 gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(help)->vbox), text);
200 gtk_label_set_selectable(GTK_LABEL(text), TRUE);
201 gtk_label_set_markup(GTK_LABEL(text), _(
202 "<u>Quick Start</u>\n"
203 "Just put the name of the file you're looking for in single quotes:\n"
204 "<b>'index.html'</b> (to find a file called 'index.html')\n"
205 "\n"
206 "<u>Examples</u>\n"
207 "<b>'*.htm', '*.html'</b> (finds HTML files)\n"
208 "<b>IsDir 'lib'</b> (finds directories called 'lib')\n"
209 "<b>IsReg 'core'</b> (finds a regular file called 'core')\n"
210 "<b>! (IsDir, IsReg)</b> (is neither a directory nor a regular file)\n"
211 "<b>mtime after 1 day ago and size > 1Mb</b> (big, and recently modified)\n"
212 "<b>'CVS' prune, isreg</b> (a regular file not in CVS)\n"
213 "<b>IsReg system(grep -q fred \"%\")</b> (contains the word 'fred')\n"
214 "\n"
215 "<u>Simple Tests</u>\n"
216 "<b>IsReg, IsLink, IsDir, IsChar, IsBlock, IsDev, IsPipe, IsSocket, IsDoor</b> "
217 "(types)\n"
218 "<b>IsSUID, IsSGID, IsSticky, IsReadable, IsWriteable, IsExecutable</b> "
219 "(permissions)\n"
220 "<b>IsEmpty, IsMine</b>\n"
221 "A pattern in single quotes is a shell-style wildcard pattern to match. If it\n"
222 "contains a slash then the match is against the full path; otherwise it is\n"
223 "against the leafname only.\n"
224 "\n"
225 "<u>Comparisons</u>\n"
226 "<b><, <=, =, !=, >, >=, After, Before</b> (compare two values)\n"
227 "<b>5 bytes, 1Kb, 2Mb, 3Gb</b> (file sizes)\n"
228 "<b>2 secs|mins|hours|days|weeks|years ago|hence</b> (times)\n"
229 "<b>atime, ctime, mtime, now, size, inode, nlinks, uid, gid, blocks</b> "
230 "(values)\n"
231 "\n"
232 "<u>Specials</u>\n"
233 "<b>system(command)</b> (true if 'command' returns with a zero exit status;\n"
234 "a % in 'command' is replaced with the path of the current file)\n"
235 "<b>prune</b> (false, and prevents searching the contents of a directory)."));
236
237 g_signal_connect(help, "response",
238 G_CALLBACK(gtk_widget_destroy), NULL);
239
240 gtk_widget_show_all(help);
241 }
242
show_chmod_help(gpointer data)243 static void show_chmod_help(gpointer data)
244 {
245 GtkWidget *help;
246 GtkWidget *text;
247
248 help = gtk_dialog_new_with_buttons(
249 _("Change permissions reference"),
250 NULL, 0,
251 GTK_STOCK_CLOSE, GTK_RESPONSE_CANCEL,
252 NULL);
253 gtk_dialog_set_default_response(GTK_DIALOG(help), GTK_RESPONSE_CANCEL);
254
255 text = gtk_label_new(NULL);
256 gtk_misc_set_padding(GTK_MISC(text), 2, 2);
257 gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(help)->vbox), text);
258 gtk_label_set_selectable(GTK_LABEL(text), TRUE);
259 gtk_label_set_markup(GTK_LABEL(text), _(
260 "Normally, you can just select a command from the menu (click \n"
261 "on the arrow beside the command box). Sometimes, you need more...\n"
262 "\n"
263 "The format of a command is: <b>CHANGE, CHANGE, ...</b>\n"
264 "Each <b>CHANGE</b> is: <b>WHO HOW PERMISSIONS</b>\n"
265 "<b>WHO</b> is some combination of <b>u</b>, <b>g</b> and <b>o</b> which "
266 "determines whether to\n"
267 "change the permissions for the User (owner), Group or Others.\n"
268 "<b>HOW</b> is <b>+</b>, <b>-</b> or <b>=</b> to add, remove or set "
269 "exactly the permissions.\n"
270 "<b>PERMISSIONS</b> is some combination of the letters <b>rwxXstugo</b>\n"
271 "\n"
272 "Bracketed text and spaces are ignored.\n"
273 "\n"
274 "<u>Examples</u>\n"
275 "<b>u+rw</b>: the file owner gains read and write permission\n"
276 "<b>g=u</b>: the group permissions are set to be the same as the user's\n"
277 "<b>o=u-w</b>: others get the same permissions as the owner, but without "
278 "write permission\n"
279 "<b>a+x</b>: <b>a</b>ll get execute/access permission - same as <b>ugo+x</b>\n"
280 "<b>a+X</b>: directories become accessable by everyone; files which were\n"
281 "executable by anyone become executable by everyone\n"
282 "<b>u+rw, go+r</b>: two commands at once!\n"
283 "<b>u+s</b>: set the SetUID bit - often has no effect on script files\n"
284 "<b>755</b>: set the permissions directly\n"
285 "\n"
286 "See the chmod(1) man page for full details."));
287
288 g_signal_connect(help, "response",
289 G_CALLBACK(gtk_widget_destroy), NULL);
290
291 gtk_widget_show_all(help);
292 }
293
294
show_settype_help(gpointer data)295 static void show_settype_help(gpointer data)
296 {
297 GtkWidget *help;
298 GtkWidget *text;
299
300 help = gtk_dialog_new_with_buttons(
301 _("Set type reference"),
302 NULL, 0,
303 GTK_STOCK_CLOSE, GTK_RESPONSE_CANCEL,
304 NULL);
305 gtk_dialog_set_default_response(GTK_DIALOG(help), GTK_RESPONSE_CANCEL);
306
307 text = gtk_label_new(NULL);
308 gtk_misc_set_padding(GTK_MISC(text), 2, 2);
309 gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(help)->vbox), text);
310 gtk_label_set_selectable(GTK_LABEL(text), TRUE);
311 gtk_label_set_markup(GTK_LABEL(text), _(
312 "Normally ROX-Filer determines the type of a regular file\n"
313 "by matching it's name against a pattern. To change the\n"
314 "type of the file you must rename it.\n"
315 "\n"
316 "Newer file systems can support something called 'Extended\n"
317 "Attributes' which can be used to store additional data with\n"
318 "each file as named parameters. ROX-Filer uses the\n"
319 "'user.mime_type' attribute to store file types.\n"
320 "\n"
321 "File types are only supported for regular files, not\n"
322 "directories, devices, pipes or sockets, and then only\n"
323 "on certain file systems and where the OS implements them.\n"));
324
325 text = gtk_label_new(_(ATTR_MAN_PAGE));
326 gtk_misc_set_padding(GTK_MISC(text), 2, 2);
327 gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(help)->vbox), text);
328
329 g_signal_connect(help, "response",
330 G_CALLBACK(gtk_widget_destroy), NULL);
331
332 gtk_widget_show_all(help);
333 }
334
process_message(GUIside * gui_side,const gchar * buffer)335 static void process_message(GUIside *gui_side, const gchar *buffer)
336 {
337 ABox *abox = gui_side->abox;
338
339 if (*buffer == '?')
340 abox_ask(abox, buffer + 1);
341 else if (*buffer == 's')
342 dir_check_this(buffer + 1); /* Update this item */
343 else if (*buffer == '=')
344 abox_add_filename(abox, buffer + 1);
345 else if (*buffer == '#')
346 abox_clear_results(abox);
347 else if (*buffer == 'X')
348 {
349 filer_close_recursive(buffer + 1);
350 /* Let child know it's safe to continue... */
351 fputc('X', gui_side->to_child);
352 fflush(gui_side->to_child);
353 }
354 else if (*buffer == 'm' || *buffer == 'M')
355 {
356 /* Mount / major changes to this path */
357 if (*buffer == 'M')
358 {
359 mount_update(TRUE);
360 mount_user_mount(buffer + 1);
361 }
362 filer_check_mounted(buffer + 1);
363 }
364 else if (*buffer == '/')
365 abox_set_current_object(abox, buffer + 1);
366 else if (*buffer == 'o')
367 filer_opendir(buffer + 1, NULL, NULL);
368 else if (*buffer == '!')
369 {
370 gui_side->errors++;
371 abox_log(abox, buffer + 1, "error");
372 }
373 else if (*buffer == '<')
374 abox_set_file(abox, 0, buffer+1);
375 else if (*buffer == '>')
376 {
377 abox_set_file(abox, 1, buffer+1);
378 abox_show_compare(abox, TRUE);
379 }
380 else if (*buffer == '%')
381 {
382 abox_set_percentage(abox, atoi(buffer+1));
383 }
384 else
385 abox_log(abox, buffer + 1, NULL);
386 }
387
388 /* Called when the child sends us a message */
message_from_child(gpointer data,gint source,GdkInputCondition condition)389 static void message_from_child(gpointer data,
390 gint source,
391 GdkInputCondition condition)
392 {
393 char buf[5];
394 GUIside *gui_side = (GUIside *) data;
395 ABox *abox = gui_side->abox;
396 GtkTextBuffer *text_buffer;
397
398 text_buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(abox->log));
399
400 if (read_exact(source, buf, 4))
401 {
402 ssize_t message_len;
403 char *buffer;
404
405 buf[4] = '\0';
406 message_len = strtol(buf, NULL, 16);
407 buffer = g_malloc(message_len + 1);
408 if (message_len > 0 && read_exact(source, buffer, message_len))
409 {
410 buffer[message_len] = '\0';
411 process_message(gui_side, buffer);
412 g_free(buffer);
413 return;
414 }
415 g_printerr("Child died in the middle of a message.\n");
416 }
417
418 if (gui_side->abort_attempts)
419 abox_log(abox, _("\nProcess terminated.\n"), "error");
420
421 /* The child is dead */
422 gui_side->child = 0;
423
424 fclose(gui_side->to_child);
425 gui_side->to_child = NULL;
426 close(gui_side->from_child);
427 g_source_remove(gui_side->input_tag);
428 abox_cancel_ask(gui_side->abox);
429
430 if (gui_side->errors)
431 {
432 guchar *report;
433
434 if (gui_side->errors == 1)
435 report = g_strdup(_("There was one error.\n"));
436 else
437 report = g_strdup_printf(_("There were %d errors.\n"),
438 gui_side->errors);
439
440 gtk_text_buffer_insert_at_cursor(text_buffer, report, -1);
441
442 g_free(report);
443 }
444 else if (gui_side->show_info == FALSE)
445 gtk_widget_destroy(GTK_WIDGET(gui_side->abox));
446 }
447
448 /* Scans src_dir, calling cb(item, dest_path) for each item */
for_dir_contents(ForDirCB * cb,const char * src_dir,const char * dest_path)449 static void for_dir_contents(ForDirCB *cb,
450 const char *src_dir,
451 const char *dest_path)
452 {
453 DIR *d;
454 struct dirent *ent;
455 GList *list = NULL, *next;
456
457 d = mc_opendir(src_dir);
458 if (!d)
459 {
460 /* Message displayed is "ERROR reading 'path': message" */
461 printf_send("!%s '%s': %s\n", _("ERROR reading"),
462 src_dir, g_strerror(errno));
463 return;
464 }
465
466 send_dir(src_dir);
467
468 while ((ent = mc_readdir(d)))
469 {
470 if (ent->d_name[0] == '.' && (ent->d_name[1] == '\0'
471 || (ent->d_name[1] == '.' && ent->d_name[2] == '\0')))
472 continue;
473 list = g_list_prepend(list, g_strdup(make_path(src_dir,
474 ent->d_name)));
475 }
476 mc_closedir(d);
477
478 for (next = list; next; next = next->next)
479 {
480 cb((char *) next->data, dest_path);
481
482 g_free(next->data);
483 }
484 g_list_free(list);
485 }
486
487 /* Read this many bytes into the buffer. TRUE on success. */
read_exact(int source,char * buffer,ssize_t len)488 static gboolean read_exact(int source, char *buffer, ssize_t len)
489 {
490 while (len > 0)
491 {
492 ssize_t got;
493 got = read(source, buffer, len);
494 if (got < 1)
495 return FALSE;
496 len -= got;
497 buffer += got;
498 }
499 return TRUE;
500 }
501
send_done(void)502 static void send_done(void)
503 {
504 printf_send(_("'\nDone\n"));
505 }
506
507 /* Notify the filer that this item has been updated */
send_check_path(const gchar * path)508 static void send_check_path(const gchar *path)
509 {
510 printf_send("s%s", path);
511 }
512
513 /* Notify the filer that this whole subtree has changed (eg, been unmounted) */
send_mount_path(const gchar * path)514 static void send_mount_path(const gchar *path)
515 {
516 printf_send("m%s", path);
517 }
518
519 /* Send a message to the filer process. The first character indicates the
520 * type of the message.
521 */
printf_send(const char * msg,...)522 static gboolean printf_send(const char *msg, ...)
523 {
524 va_list args;
525 gchar *tmp;
526
527 va_start(args, msg);
528 tmp = g_strdup_vprintf(msg, args);
529 va_end(args);
530
531 g_string_assign(message, tmp);
532 g_free(tmp);
533
534 return send_msg();
535 }
536
537 /* Send 'message' to our parent process. TRUE on success. */
send_msg(void)538 static gboolean send_msg(void)
539 {
540 char len_buffer[5];
541 ssize_t len;
542
543 g_return_val_if_fail(message->len < 0xffff, FALSE);
544
545 sprintf(len_buffer, "%04" G_GSIZE_MODIFIER "x", message->len);
546 fwrite(len_buffer, 1, 4, to_parent);
547 len = fwrite(message->str, 1, message->len, to_parent);
548 fflush(to_parent);
549 return len == (ssize_t) message->len;
550 }
551
552 /* Set the directory indicator at the top of the window */
send_dir(const char * dir)553 static gboolean send_dir(const char *dir)
554 {
555 return printf_send("/%s", dir);
556 }
557
send_error(void)558 static gboolean send_error(void)
559 {
560 return printf_send("!%s: %s\n", _("ERROR"), g_strerror(errno));
561 }
562
response(GtkDialog * dialog,gint response,GUIside * gui_side)563 static void response(GtkDialog *dialog, gint response, GUIside *gui_side)
564 {
565 gchar code;
566
567 if (!gui_side->to_child)
568 return;
569
570 if (response == GTK_RESPONSE_YES)
571 code = 'Y';
572 else if (response == GTK_RESPONSE_NO)
573 code = 'N';
574 else
575 return;
576
577 fputc(code, gui_side->to_child);
578 fflush(gui_side->to_child);
579 abox_show_compare(gui_side->abox, FALSE);
580 }
581
flag_toggled(ABox * abox,gint flag,GUIside * gui_side)582 static void flag_toggled(ABox *abox, gint flag, GUIside *gui_side)
583 {
584 if (!gui_side->to_child)
585 return;
586
587 fputc(flag, gui_side->to_child);
588 fflush(gui_side->to_child);
589 }
590
read_new_entry_text(void)591 static void read_new_entry_text(void)
592 {
593 int len;
594 char c;
595 GString *new;
596
597 new = g_string_new(NULL);
598
599 for (;;)
600 {
601 len = read(from_parent, &c, 1);
602 if (len != 1)
603 {
604 fprintf(stderr, "read() error: %s\n",
605 g_strerror(errno));
606 _exit(1); /* Parent died? */
607 }
608
609 if (c == '\n')
610 break;
611 g_string_append_c(new, c);
612 }
613
614 g_free(new_entry_string);
615 new_entry_string = new->str;
616 g_string_free(new, FALSE);
617 }
618
process_flag(char flag)619 static void process_flag(char flag)
620 {
621 switch (flag)
622 {
623 case 'Q':
624 quiet = !quiet;
625 break;
626 case 'F':
627 o_force = !o_force;
628 break;
629 case 'R':
630 o_recurse = !o_recurse;
631 break;
632 case 'B':
633 o_brief = !o_brief;
634 break;
635 case 'W':
636 o_newer = !o_newer;
637 break;
638 case 'E':
639 read_new_entry_text();
640 break;
641 default:
642 printf_send("!ERROR: Bad message '%c'\n", flag);
643 break;
644 }
645 }
646
647 /* If the parent has sent any flag toggles, read them */
check_flags(void)648 static void check_flags(void)
649 {
650 fd_set set;
651 int got;
652 char retval;
653 struct timeval tv;
654
655 FD_ZERO(&set);
656
657 while (1)
658 {
659 FD_SET(from_parent, &set);
660 tv.tv_sec = 0;
661 tv.tv_usec = 0;
662 got = select(from_parent + 1, &set, NULL, NULL, &tv);
663
664 if (got == -1)
665 g_error("select() failed: %s\n", g_strerror(errno));
666 else if (!got)
667 return;
668
669 got = read(from_parent, &retval, 1);
670 if (got != 1)
671 g_error("read() error: %s\n", g_strerror(errno));
672
673 process_flag(retval);
674 }
675 }
676
677 /* Read until the user sends a reply. If ignore_quiet is TRUE then
678 * the user MUST click Yes or No, else treat quiet on as Yes.
679 * If the user needs prompting then does send_msg().
680 */
printf_reply(int fd,gboolean ignore_quiet,const char * msg,...)681 static gboolean printf_reply(int fd, gboolean ignore_quiet,
682 const char *msg, ...)
683 {
684 ssize_t len;
685 char retval;
686 va_list args;
687 gchar *tmp;
688
689 if (quiet && !ignore_quiet)
690 return TRUE;
691
692 va_start(args, msg);
693 tmp = g_strdup_vprintf(msg, args);
694 va_end(args);
695
696 g_string_assign(message, tmp);
697 g_free(tmp);
698
699 send_msg();
700
701 while (1)
702 {
703 len = read(fd, &retval, 1);
704 if (len != 1)
705 {
706 fprintf(stderr, "read() error: %s\n",
707 g_strerror(errno));
708 _exit(1); /* Parent died? */
709 }
710
711 switch (retval)
712 {
713 case 'Y':
714 printf_send("' %s\n", _("Yes"));
715 return TRUE;
716 case 'N':
717 printf_send("' %s\n", _("No"));
718 return FALSE;
719 default:
720 process_flag(retval);
721 break;
722 }
723 }
724 }
725
abort_operation(GtkWidget * widget,gpointer data)726 static void abort_operation(GtkWidget *widget, gpointer data)
727 {
728 GUIside *gui_side = (GUIside *) data;
729
730 if (gui_side->child)
731 {
732 if (gui_side->abort_attempts == 0)
733 {
734 abox_log(ABOX(widget),
735 _("\nAsking child process to terminate...\n"),
736 "error");
737 kill(-gui_side->child, SIGTERM);
738 }
739 else
740 {
741 abox_log(ABOX(widget),
742 _("\nTrying to KILL run-away process...\n"),
743 "error");
744 kill(-gui_side->child, SIGKILL);
745 kill(-gui_side->child, SIGCONT);
746 }
747 gui_side->abort_attempts++;
748 }
749 else
750 gtk_widget_destroy(widget);
751 }
752
destroy_action_window(GtkWidget * widget,gpointer data)753 static void destroy_action_window(GtkWidget *widget, gpointer data)
754 {
755 GUIside *gui_side = (GUIside *) data;
756
757 if (gui_side->child)
758 {
759 kill(-gui_side->child, SIGTERM);
760 fclose(gui_side->to_child);
761 close(gui_side->from_child);
762 g_source_remove(gui_side->input_tag);
763 }
764
765 g_free(gui_side);
766
767 one_less_window();
768 }
769
770 /* Create two pipes, fork() a child and return a pointer to a GUIside struct
771 * (NULL on failure). The child calls func().
772 */
start_action(GtkWidget * abox,ActionChild * func,gpointer data,int force,int brief,int recurse,int newer)773 static GUIside *start_action(GtkWidget *abox, ActionChild *func, gpointer data,
774 int force, int brief, int recurse, int newer)
775 {
776 gboolean autoq;
777 int filedes[4]; /* 0 and 2 are for reading */
778 GUIside *gui_side;
779 pid_t child;
780 struct sigaction act;
781
782 if (pipe(filedes))
783 {
784 report_error("pipe: %s", g_strerror(errno));
785 gtk_widget_destroy(abox);
786 return NULL;
787 }
788
789 if (pipe(filedes + 2))
790 {
791 close(filedes[0]);
792 close(filedes[1]);
793 report_error("pipe: %s", g_strerror(errno));
794 gtk_widget_destroy(abox);
795 return NULL;
796 }
797
798 autoq = gtk_toggle_button_get_active(
799 GTK_TOGGLE_BUTTON(ABOX(abox)->quiet));
800
801 o_force = force;
802 o_brief = brief;
803 o_recurse = recurse;
804 o_newer = newer;
805
806 child = fork();
807 switch (child)
808 {
809 case -1:
810 report_error("fork: %s", g_strerror(errno));
811 gtk_widget_destroy(abox);
812 return NULL;
813 case 0:
814 /* We are the child */
815
816 /* Create a new process group */
817 setpgid(0, 0);
818
819 quiet = autoq;
820
821 dir_drop_all_notifies();
822
823 /* Reset the SIGCHLD handler */
824 act.sa_handler = SIG_DFL;
825 sigemptyset(&act.sa_mask);
826 act.sa_flags = 0;
827 sigaction(SIGCHLD, &act, NULL);
828
829 message = g_string_new(NULL);
830 close(filedes[0]);
831 close(filedes[3]);
832 to_parent = fdopen(filedes[1], "wb");
833 from_parent = filedes[2];
834 func(data);
835 send_dir("");
836 _exit(0);
837 }
838
839 /* We are the parent */
840 close(filedes[1]);
841 close(filedes[2]);
842 gui_side = g_new(GUIside, 1);
843 gui_side->from_child = filedes[0];
844 gui_side->to_child = fdopen(filedes[3], "wb");
845 gui_side->child = child;
846 gui_side->errors = 0;
847 gui_side->show_info = FALSE;
848 gui_side->default_string = NULL;
849 gui_side->entry_string_func = NULL;
850 gui_side->abort_attempts = 0;
851
852 gui_side->abox = ABOX(abox);
853 g_signal_connect(abox, "destroy",
854 G_CALLBACK(destroy_action_window), gui_side);
855
856 g_signal_connect(abox, "response", G_CALLBACK(response), gui_side);
857 g_signal_connect(abox, "flag_toggled",
858 G_CALLBACK(flag_toggled), gui_side);
859 g_signal_connect(abox, "abort_operation",
860 G_CALLBACK(abort_operation), gui_side);
861
862 gui_side->input_tag = gdk_input_add_full(gui_side->from_child,
863 GDK_INPUT_READ,
864 message_from_child,
865 gui_side, NULL);
866
867 return gui_side;
868 }
869
870 /* ACTIONS ON ONE ITEM */
871
872 /* These may call themselves recursively, or ask questions, etc */
873
874 /* Updates the global size_tally, file_counter and dir_counter */
do_usage(const char * src_path,const char * unused)875 static void do_usage(const char *src_path, const char *unused)
876 {
877 struct stat info;
878
879 check_flags();
880
881 if (mc_lstat(src_path, &info))
882 {
883 printf_send("'%s:\n", src_path);
884 send_error();
885 }
886 else if (S_ISREG(info.st_mode) || S_ISLNK(info.st_mode))
887 {
888 file_counter++;
889 size_tally += info.st_size;
890 }
891 else if (S_ISDIR(info.st_mode))
892 {
893 dir_counter++;
894 if (printf_reply(from_parent, FALSE,
895 _("?Count contents of %s?"), src_path))
896 {
897 char *safe_path;
898 safe_path = g_strdup(src_path);
899 for_dir_contents(do_usage, safe_path, safe_path);
900 g_free(safe_path);
901 }
902 }
903 else
904 file_counter++;
905 }
906
907 /* dest_path is the dir containing src_path */
do_delete(const char * src_path,const char * unused)908 static void do_delete(const char *src_path, const char *unused)
909 {
910 struct stat info;
911 gboolean write_prot;
912 char *safe_path;
913
914 check_flags();
915
916 if (mc_lstat(src_path, &info))
917 {
918 send_error();
919 return;
920 }
921
922 write_prot = S_ISLNK(info.st_mode) ? FALSE
923 : access(src_path, W_OK) != 0;
924 if (write_prot || !quiet)
925 {
926 int res;
927
928 printf_send("<%s", src_path);
929 printf_send(">");
930 res=printf_reply(from_parent, write_prot && !o_force,
931 _("?Delete %s'%s'?"),
932 write_prot ? _("WRITE-PROTECTED ") : "",
933 src_path);
934 printf_send("<");
935 if (!res)
936 return;
937 }
938 else if (!o_brief)
939 printf_send(_("'Deleting '%s'\n"), src_path);
940
941 safe_path = g_strdup(src_path);
942
943 if (S_ISDIR(info.st_mode))
944 {
945 for_dir_contents(do_delete, safe_path, safe_path);
946 if (rmdir(safe_path))
947 {
948 g_free(safe_path);
949 send_error();
950 return;
951 }
952 printf_send(_("'Directory '%s' deleted\n"), safe_path);
953 send_mount_path(safe_path);
954 }
955 else if (unlink(src_path))
956 send_error();
957 else
958 {
959 send_check_path(safe_path);
960 if (strcmp(g_basename(safe_path), ".DirIcon") == 0)
961 {
962 gchar *dir;
963 dir = g_path_get_dirname(safe_path);
964 send_check_path(dir);
965 g_free(dir);
966 }
967 }
968
969 g_free(safe_path);
970 }
971
do_eject(const char * path)972 static void do_eject(const char *path)
973 {
974 const char *argv[]={"sh", "-c", NULL, NULL};
975 char *err;
976
977 check_flags();
978
979 if (!quiet)
980 {
981 int res;
982 printf_send("<%s", path);
983 printf_send(">");
984 res=printf_reply(from_parent, !o_force,
985 _("?Eject '%s'?"),
986 path);
987 printf_send("<");
988 if (!res)
989 return;
990 }
991 else if (!o_brief)
992 printf_send(_("'Eject '%s'\n"), path);
993
994 /* Need to close all sub-directories now, or we
995 * can't unmount if dnotify is used.
996 */
997 {
998 char c = '?';
999 printf_send("X%s", path);
1000 /* Wait until it's safe... */
1001 read(from_parent, &c, 1);
1002 g_return_if_fail(c == 'X');
1003 }
1004
1005 argv[2] = build_command_with_path(o_action_eject_command.value,
1006 path);
1007 err = fork_exec_wait((const char**)argv);
1008 g_free((gchar *) argv[2]);
1009 if (err)
1010 {
1011 printf_send(_("!%s\neject failed\n"), err);
1012 g_free(err);
1013 }
1014
1015 printf_send("M%s", path);
1016
1017 }
1018
1019 /* path is the item to check. If is is a directory then we may recurse
1020 * (unless prune is used).
1021 */
do_find(const char * path,const char * unused)1022 static void do_find(const char *path, const char *unused)
1023 {
1024 FindInfo info;
1025
1026 check_flags();
1027
1028 if (!quiet)
1029 {
1030 if (!printf_reply(from_parent, FALSE, _("?Check '%s'?"), path))
1031 return;
1032 }
1033
1034 for (;;)
1035 {
1036 if (new_entry_string)
1037 {
1038 find_condition_free(find_condition);
1039 find_condition = find_compile(new_entry_string);
1040 null_g_free(&new_entry_string);
1041 }
1042
1043 if (find_condition)
1044 break;
1045
1046 printf_send(_("!Invalid find condition - "
1047 "change it and try again\n"));
1048 if (!printf_reply(from_parent, TRUE,
1049 _("?Check '%s'?"), path))
1050 return;
1051 }
1052
1053 if (mc_lstat(path, &info.stats))
1054 {
1055 send_error();
1056 printf_send(_("'(while checking '%s')\n"), path);
1057 return;
1058 }
1059
1060 info.fullpath = path;
1061 time(&info.now); /* XXX: Not for each check! */
1062
1063 info.leaf = g_basename(path);
1064 info.prune = FALSE;
1065 if (find_test_condition(find_condition, &info))
1066 printf_send("=%s", path);
1067
1068 if (S_ISDIR(info.stats.st_mode) && !info.prune)
1069 {
1070 char *safe_path;
1071 safe_path = g_strdup(path);
1072 for_dir_contents(do_find, safe_path, safe_path);
1073 g_free(safe_path);
1074 }
1075 }
1076
1077 /* Like mode_compile(), but ignores spaces and bracketed bits */
nice_mode_compile(const char * mode_string,unsigned int masked_ops)1078 static struct mode_change *nice_mode_compile(const char *mode_string,
1079 unsigned int masked_ops)
1080 {
1081 GString *new;
1082 int brackets = 0;
1083 struct mode_change *retval = NULL;
1084
1085 new = g_string_new(NULL);
1086
1087 for (; *mode_string; mode_string++)
1088 {
1089 if (*mode_string == '(')
1090 brackets++;
1091 if (*mode_string == ')')
1092 {
1093 brackets--;
1094 if (brackets < 0)
1095 break;
1096 continue;
1097 }
1098
1099 if (brackets == 0 && *mode_string != ' ')
1100 g_string_append_c(new, *mode_string);
1101 }
1102
1103 if (brackets == 0)
1104 retval = mode_compile(new->str, masked_ops);
1105 g_string_free(new, TRUE);
1106 return retval;
1107 }
1108
do_chmod(const char * path,const char * unused)1109 static void do_chmod(const char *path, const char *unused)
1110 {
1111 struct stat info;
1112 mode_t new_mode;
1113
1114 check_flags();
1115
1116 if (mc_lstat(path, &info))
1117 {
1118 send_error();
1119 return;
1120 }
1121 if (S_ISLNK(info.st_mode))
1122 return;
1123
1124 if (!quiet)
1125 {
1126 int res;
1127 printf_send("<%s", path);
1128 printf_send(">");
1129 res=printf_reply(from_parent, FALSE,
1130 _("?Change permissions of '%s'?"), path);
1131 printf_send("<");
1132 if (!res)
1133 return;
1134 }
1135 else if (!o_brief)
1136 printf_send(_("'Changing permissions of '%s'\n"), path);
1137
1138 for (;;)
1139 {
1140 if (new_entry_string)
1141 {
1142 if (mode_change)
1143 mode_free(mode_change);
1144 mode_change = nice_mode_compile(new_entry_string,
1145 MODE_MASK_ALL);
1146 null_g_free(&new_entry_string);
1147 }
1148
1149 if (mode_change)
1150 break;
1151
1152 printf_send(
1153 _("!Invalid mode command - change it and try again\n"));
1154 if (!printf_reply(from_parent, TRUE,
1155 _("?Change permissions of '%s'?"), path))
1156 return;
1157 }
1158
1159 if (mc_lstat(path, &info))
1160 {
1161 send_error();
1162 return;
1163 }
1164 if (S_ISLNK(info.st_mode))
1165 return;
1166
1167 new_mode = mode_adjust(info.st_mode, mode_change);
1168 if (chmod(path, new_mode))
1169 {
1170 send_error();
1171 return;
1172 }
1173
1174 send_check_path(path);
1175
1176 if (S_ISDIR(info.st_mode))
1177 {
1178 send_mount_path(path);
1179
1180 if (o_recurse)
1181 {
1182 guchar *safe_path;
1183 safe_path = g_strdup(path);
1184 for_dir_contents(do_chmod, safe_path, safe_path);
1185 g_free(safe_path);
1186 }
1187 }
1188 }
1189
do_settype(const char * path,const char * unused)1190 static void do_settype(const char *path, const char *unused)
1191 {
1192 struct stat info;
1193
1194 check_flags();
1195
1196 if (mc_lstat(path, &info))
1197 {
1198 send_error();
1199 return;
1200 }
1201 if (S_ISLNK(info.st_mode))
1202 return;
1203
1204 if (!quiet)
1205 {
1206 int res;
1207 printf_send("<%s", path);
1208 printf_send(">");
1209 if (S_ISDIR(info.st_mode))
1210 res=printf_reply(from_parent, FALSE,
1211 _("?Change contents of '%s'?"), path);
1212 else
1213 res=printf_reply(from_parent, FALSE,
1214 _("?Change type of '%s'?"), path);
1215 printf_send("<");
1216 if (!res)
1217 return;
1218 }
1219
1220 for (;;)
1221 {
1222 if (new_entry_string)
1223 {
1224 type_change = mime_type_lookup(new_entry_string);
1225 null_g_free(&new_entry_string);
1226 }
1227
1228 if (type_change)
1229 break;
1230
1231 printf_send(_("!Invalid type - "
1232 "change it and try again\n"));
1233 if (!printf_reply(from_parent, TRUE,
1234 _("?Change type of '%s'?"), path))
1235 return;
1236 }
1237
1238 if (mc_lstat(path, &info))
1239 {
1240 send_error();
1241 return;
1242 }
1243 if (S_ISLNK(info.st_mode))
1244 return;
1245
1246 if (S_ISREG(info.st_mode))
1247 {
1248 if (!o_brief)
1249 {
1250 const char *comment;
1251
1252 comment = mime_type_comment(type_change);
1253 printf_send(_("'Changing type of '%s' to '%s'\n"), path,
1254 comment);
1255 }
1256
1257 if (xtype_set(path, type_change))
1258 {
1259 send_error();
1260 return;
1261 }
1262
1263 send_check_path(path);
1264 }
1265 else if (S_ISDIR(info.st_mode))
1266 {
1267 if (o_recurse)
1268 {
1269 guchar *safe_path;
1270 safe_path = g_strdup(path);
1271 for_dir_contents(do_settype, safe_path, unused);
1272 g_free(safe_path);
1273 }
1274 else if(!o_brief)
1275 {
1276 printf_send(_("'Not changing type of directory '%s'\n"),
1277 path);
1278 }
1279 }
1280 else if(!o_brief)
1281 {
1282 printf_send(_("'Non-regular file '%s' not changed\n"),
1283 path);
1284 }
1285 }
1286
1287 /* We want to copy 'object' into directory 'dir'. If 'action_leaf'
1288 * is set then that is the new leafname, otherwise the leafname stays
1289 * the same.
1290 */
make_dest_path(const char * object,const char * dir)1291 static const char *make_dest_path(const char *object, const char *dir)
1292 {
1293 const char *leaf;
1294
1295 if (action_leaf)
1296 leaf = action_leaf;
1297 else
1298 {
1299 leaf = strrchr(object, '/');
1300 if (!leaf)
1301 leaf = object; /* Error? */
1302 else
1303 leaf++;
1304 }
1305
1306 return make_path(dir, leaf);
1307 }
1308
1309 /* If action_leaf is not NULL it specifies the new leaf name */
do_copy2(const char * path,const char * dest)1310 static void do_copy2(const char *path, const char *dest)
1311 {
1312 const char *dest_path;
1313 struct stat info;
1314 struct stat dest_info;
1315
1316 check_flags();
1317
1318 dest_path = make_dest_path(path, dest);
1319
1320 if (mc_lstat(path, &info))
1321 {
1322 send_error();
1323 return;
1324 }
1325
1326 if (mc_lstat(dest_path, &dest_info) == 0)
1327 {
1328 int err;
1329 gboolean merge;
1330
1331 merge = S_ISDIR(info.st_mode) && S_ISDIR(dest_info.st_mode);
1332
1333 if (!merge && o_newer && info.st_mtime > dest_info.st_mtime)
1334 {
1335 /* Newer; keep going */
1336 }
1337 else
1338 {
1339 printf_send("<%s", path);
1340 printf_send(">%s", dest_path);
1341 if (!printf_reply(from_parent, TRUE,
1342 _("?'%s' already exists - %s?"),
1343 dest_path,
1344 merge ? _("merge contents")
1345 : _("overwrite")))
1346 return;
1347 }
1348
1349 if (!merge)
1350 {
1351 if (S_ISDIR(dest_info.st_mode))
1352 err = rmdir(dest_path);
1353 else
1354 err = unlink(dest_path);
1355
1356 if (err)
1357 {
1358 send_error();
1359 if (errno != ENOENT)
1360 return;
1361 printf_send(_("'Trying copy anyway...\n"));
1362 }
1363 }
1364 }
1365 else if (!quiet)
1366 {
1367 printf_send("<%s", path);
1368 printf_send(">");
1369 if (!printf_reply(from_parent, FALSE,
1370 _("?Copy %s as %s?"), path, dest_path))
1371 return;
1372 }
1373 else if (!o_brief || S_ISDIR(info.st_mode))
1374 printf_send(_("'Copying %s as %s\n"), path, dest_path);
1375
1376 if (S_ISDIR(info.st_mode))
1377 {
1378 mode_t mode = info.st_mode;
1379 char *safe_path, *safe_dest;
1380 struct stat dest_info;
1381 gboolean exists;
1382
1383 safe_path = g_strdup(path);
1384 safe_dest = g_strdup(dest_path);
1385
1386 exists = !mc_lstat(dest_path, &dest_info);
1387
1388 if (exists && !S_ISDIR(dest_info.st_mode))
1389 printf_send(_("!ERROR: Destination already exists, "
1390 "but is not a directory\n"));
1391 else if (exists == FALSE && mkdir(dest_path, 0700 | mode))
1392 send_error();
1393 else
1394 {
1395 if (!exists)
1396 /* (just been created then) */
1397 send_check_path(dest_path);
1398
1399 action_leaf = NULL;
1400 for_dir_contents(do_copy2, safe_path, safe_dest);
1401 /* Note: dest_path now invalid... */
1402
1403 if (!exists)
1404 {
1405 struct utimbuf utb;
1406
1407 /* We may have created the directory with
1408 * more permissions than the source so that
1409 * we could write to it... change it back now.
1410 */
1411 if (chmod(safe_dest, mode))
1412 {
1413 /* Some filesystems don't support
1414 * SetGID and SetUID bits. Ignore
1415 * these errors.
1416 */
1417 if (errno != EPERM)
1418 send_error();
1419 }
1420
1421 /* Also, try to preserve the timestamps */
1422 utb.actime = info.st_atime;
1423 utb.modtime = info.st_mtime;
1424
1425 utime(safe_dest, &utb);
1426 }
1427 }
1428
1429 g_free(safe_path);
1430 g_free(safe_dest);
1431 }
1432 else if (S_ISLNK(info.st_mode))
1433 {
1434 char *target;
1435
1436 /* Not all versions of cp(1) can make symlinks,
1437 * so we special-case it.
1438 */
1439
1440 target = readlink_dup(path);
1441 if (target)
1442 {
1443 if (symlink(target, dest_path))
1444 send_error();
1445 else
1446 send_check_path(dest_path);
1447
1448 g_free(target);
1449 }
1450 else
1451 send_error();
1452 }
1453 else
1454 {
1455 guchar *error;
1456
1457 error = copy_file(path, dest_path);
1458
1459 if (error)
1460 {
1461 printf_send(_("!%s\nFailed to copy '%s'\n"),
1462 error, path);
1463 g_free(error);
1464 }
1465 else
1466 send_check_path(dest_path);
1467 }
1468 }
1469
1470 /* If action_leaf is not NULL it specifies the new leaf name */
do_move2(const char * path,const char * dest)1471 static void do_move2(const char *path, const char *dest)
1472 {
1473 const char *dest_path;
1474 const char *argv[] = {"mv", "-f", NULL, NULL, NULL};
1475 struct stat info2;
1476 gboolean is_dir;
1477 char *err;
1478
1479 check_flags();
1480
1481 dest_path = make_dest_path(path, dest);
1482
1483 is_dir = mc_lstat(path, &info2) == 0 && S_ISDIR(info2.st_mode);
1484
1485 if (access(dest_path, F_OK) == 0)
1486 {
1487 struct stat info;
1488 int err;
1489
1490 if (mc_lstat(dest_path, &info))
1491 {
1492 send_error();
1493 return;
1494 }
1495
1496 if (!is_dir && o_newer && info2.st_mtime > info.st_mtime)
1497 {
1498 /* Newer; keep going */
1499 }
1500 else
1501 {
1502 printf_send("<%s", path);
1503 printf_send(">%s", dest_path);
1504 if (!printf_reply(from_parent, TRUE,
1505 _("?'%s' already exists - overwrite?"),
1506 dest_path))
1507 return;
1508 }
1509
1510 if (S_ISDIR(info.st_mode))
1511 err = rmdir(dest_path);
1512 else
1513 err = unlink(dest_path);
1514
1515 if (err)
1516 {
1517 send_error();
1518 if (errno != ENOENT)
1519 return;
1520 printf_send(_("'Trying move anyway...\n"));
1521 }
1522 }
1523 else if (!quiet)
1524 {
1525 printf_send("<%s", path);
1526 printf_send(">");
1527 if (!printf_reply(from_parent, FALSE,
1528 _("?Move %s as %s?"), path, dest_path))
1529 return;
1530 }
1531 else if (!o_brief)
1532 printf_send(_("'Moving %s as %s\n"), path, dest_path);
1533
1534 argv[2] = path;
1535 argv[3] = dest_path;
1536
1537 err = fork_exec_wait(argv);
1538 if (err)
1539 {
1540 printf_send(_("!%s\nFailed to move %s as %s\n"),
1541 err, path, dest_path);
1542 g_free(err);
1543 }
1544 else
1545 {
1546 send_check_path(dest_path);
1547
1548 if (is_dir)
1549 send_mount_path(path);
1550 else
1551 send_check_path(path);
1552 }
1553 }
1554
1555 /* Copy path to dest.
1556 * Check that path not copied into itself.
1557 */
do_copy(const char * path,const char * dest)1558 static void do_copy(const char *path, const char *dest)
1559 {
1560 if (is_sub_dir(make_dest_path(path, dest), path))
1561 printf_send(_("!ERROR: Can't copy object into itself\n"));
1562 else
1563 {
1564 do_copy2(path, dest);
1565 send_check_path(dest);
1566 }
1567 }
1568
1569 /* Move path to dest.
1570 * Check that path not moved into itself.
1571 */
do_move(const char * path,const char * dest)1572 static void do_move(const char *path, const char *dest)
1573 {
1574 if (is_sub_dir(make_dest_path(path, dest), path))
1575 printf_send(
1576 _("!ERROR: Can't move/rename object into itself\n"));
1577 else
1578 {
1579 do_move2(path, dest);
1580 send_check_path(dest);
1581 }
1582 }
1583
1584 /* Common code for do_link_relative() and do_link_absolute(). */
do_link(const char * path,const char * dest_path)1585 static void do_link(const char *path, const char *dest_path)
1586 {
1587 if (quiet)
1588 printf_send(_("'Linking %s as %s\n"), path, dest_path);
1589 else {
1590 printf_send("<%s", path);
1591 printf_send(">");
1592 if (!printf_reply(from_parent, FALSE,
1593 _("?Link %s as %s?"), path, dest_path))
1594 return;
1595 }
1596
1597 if (symlink(path, dest_path))
1598 send_error();
1599 else
1600 send_check_path(dest_path);
1601 }
1602
do_link_relative(const char * path,const char * dest)1603 static void do_link_relative(const char *path, const char *dest)
1604 {
1605 char *rel_path;
1606 const char *dest_path;
1607
1608 dest_path = make_dest_path(path, dest);
1609
1610 check_flags();
1611
1612 rel_path = get_relative_path(dest_path, path);
1613 do_link(rel_path, dest_path);
1614 g_free(rel_path);
1615 }
1616
do_link_absolute(const char * path,const char * dest)1617 static void do_link_absolute(const char *path, const char *dest)
1618 {
1619 check_flags();
1620 do_link(path, make_dest_path(path, dest));
1621 }
1622
1623 /* Mount/umount this item (depending on 'mount') */
do_mount(const guchar * path,gboolean mount)1624 static void do_mount(const guchar *path, gboolean mount)
1625 {
1626 const char *argv[] = {"sh", "-c", NULL, NULL};
1627 char *err;
1628
1629 check_flags();
1630
1631 argv[2] = build_command_with_path(mount ? o_action_mount_command.value
1632 : o_action_umount_command.value,
1633 path);
1634
1635 if (quiet)
1636 printf_send(mount ? _("'Mounting %s\n")
1637 : _("'Unmounting %s\n"),
1638 path);
1639 else if (!printf_reply(from_parent, FALSE,
1640 mount ? _("?Mount %s?")
1641 : _("?Unmount %s?"),
1642 path))
1643 return;
1644
1645 if (!mount)
1646 {
1647 char c = '?';
1648 /* Need to close all sub-directories now, or we
1649 * can't unmount if dnotify is used.
1650 */
1651 printf_send("X%s", path);
1652 /* Wait until it's safe... */
1653 read(from_parent, &c, 1);
1654 g_return_if_fail(c == 'X');
1655 }
1656
1657 err = fork_exec_wait(argv);
1658 g_free((gchar *) argv[2]);
1659 if (err)
1660 {
1661 printf_send(mount ?
1662 _("!%s\nMount failed\n") :
1663 _("!%s\nUnmount failed\n"), err);
1664 g_free(err);
1665
1666 /* Mount may have worked even on error, eg if we try to mount
1667 * a read-only disk read/write, it gets mounted read-only
1668 * with an error.
1669 */
1670 if (mount && mount_is_mounted(path, NULL, NULL))
1671 printf_send(_("'(seems to be mounted now anyway)\n"));
1672 else
1673 return;
1674 }
1675
1676 printf_send("M%s", path);
1677 if (mount && mount_open_dir)
1678 printf_send("o%s", path);
1679 }
1680
1681 /* CHILD MAIN LOOPS */
1682
1683 /* After forking, the child calls one of these functions */
1684
1685 /* We use a double for total size in order to count beyond 4Gb */
usage_cb(gpointer data)1686 static void usage_cb(gpointer data)
1687 {
1688 GList *paths = (GList *) data;
1689 double total_size = 0;
1690 int n, i, per;
1691
1692 n=g_list_length(paths);
1693 dir_counter = file_counter = 0;
1694
1695 for (i=0; paths; paths = paths->next, i++)
1696 {
1697 guchar *path = (guchar *) paths->data;
1698
1699 send_dir(path);
1700
1701 size_tally = 0;
1702
1703 if(n>1 && i>0)
1704 {
1705 per=100*i/n;
1706 printf_send("%%%d", per);
1707 }
1708 do_usage(path, NULL);
1709
1710 printf_send("'%s: %s\n",
1711 g_basename(path),
1712 format_double_size(size_tally));
1713 total_size += size_tally;
1714 }
1715 printf_send("%%-1");
1716
1717 g_string_printf(message, _("'\nTotal: %s ("),
1718 format_double_size(total_size));
1719
1720 if (file_counter)
1721 g_string_append_printf(message,
1722 "%ld %s%s", file_counter,
1723 file_counter == 1 ? _("file") : _("files"),
1724 dir_counter ? ", " : ")\n");
1725
1726 if (file_counter == 0 && dir_counter == 0)
1727 g_string_append(message, _("no directories)\n"));
1728 else if (dir_counter)
1729 g_string_append_printf(message,
1730 "%ld %s)\n", dir_counter,
1731 dir_counter == 1 ? _("directory")
1732 : _("directories"));
1733
1734 send_msg();
1735 }
1736
1737 #ifdef DO_MOUNT_POINTS
mount_cb(gpointer data)1738 static void mount_cb(gpointer data)
1739 {
1740 GList *paths = (GList *) data;
1741 gboolean mount_points = FALSE;
1742 int n, i, per;
1743
1744 n=g_list_length(paths);
1745 for (i=0; paths; paths = paths->next, i++)
1746 {
1747 guchar *path = (guchar *) paths->data;
1748 guchar *target;
1749
1750 target = pathdup(path);
1751 if (!target)
1752 target = path;
1753
1754 if(n>1 && i>0)
1755 {
1756 per=100*i/n;
1757 printf_send("%%%d", per);
1758 }
1759 if (mount_is_mounted(target, NULL, NULL) ||
1760 g_hash_table_lookup(fstab_mounts, target))
1761 {
1762 mount_points = TRUE;
1763 do_mount(target, mount_mount); /* Mount */
1764 }
1765
1766 if (target != path)
1767 g_free(target);
1768 }
1769
1770 if (mount_points)
1771 send_done();
1772 else
1773 printf_send(_("!No mount points selected!\n"));
1774 }
1775 #endif
1776
1777 /* (use g_dirname() instead?) */
dirname(guchar * path)1778 static guchar *dirname(guchar *path)
1779 {
1780 guchar *slash;
1781
1782 slash = strrchr(path, '/');
1783 g_return_val_if_fail(slash != NULL, g_strdup(path));
1784
1785 if (slash != path)
1786 return g_strndup(path, slash - path);
1787 return g_strdup("/");
1788 }
1789
delete_cb(gpointer data)1790 static void delete_cb(gpointer data)
1791 {
1792 GList *paths = (GList *) data;
1793 int n, i, per;
1794
1795 n=g_list_length(paths);
1796 for (i=0; paths; paths = paths->next, i++)
1797 {
1798 guchar *path = (guchar *) paths->data;
1799 guchar *dir;
1800
1801 dir = dirname(path);
1802 send_dir(dir);
1803
1804 if(n>1 && i>0)
1805 {
1806 per=100*i/n;
1807 printf_send("%%%d", per);
1808 }
1809 do_delete(path, dir);
1810
1811 g_free(dir);
1812 }
1813
1814 send_done();
1815 }
1816
eject_cb(gpointer data)1817 static void eject_cb(gpointer data)
1818 {
1819 GList *paths = (GList *) data;
1820 int n, i, per;
1821
1822 n=g_list_length(paths);
1823
1824 for (i=0; paths; paths = paths->next, i++)
1825 {
1826 guchar *path = (guchar *) paths->data;
1827
1828 if(n>1 && i>0)
1829 {
1830 per=100*i/n;
1831 printf_send("%%%d", per);
1832 }
1833 send_dir(path);
1834
1835 do_eject(path);
1836 }
1837
1838 send_done();
1839 }
1840
find_cb(gpointer data)1841 static void find_cb(gpointer data)
1842 {
1843 GList *all_paths = (GList *) data;
1844 GList *paths;
1845
1846 while (1)
1847 {
1848 for (paths = all_paths; paths; paths = paths->next)
1849 {
1850 guchar *path = (guchar *) paths->data;
1851
1852 send_dir(path);
1853
1854 do_find(path, NULL);
1855 }
1856
1857 if (!printf_reply(from_parent, TRUE,
1858 _("?Another search?")))
1859 break;
1860 printf_send("#");
1861 }
1862
1863 send_done();
1864 }
1865
chmod_cb(gpointer data)1866 static void chmod_cb(gpointer data)
1867 {
1868 GList *paths = (GList *) data;
1869 int n, i, per;
1870
1871 n=g_list_length(paths);
1872
1873 for (i=0; paths; paths = paths->next, i++)
1874 {
1875 guchar *path = (guchar *) paths->data;
1876 struct stat info;
1877
1878 if(n>1 && i>0)
1879 {
1880 per=100*i/n;
1881 printf_send("%%%d", per);
1882 }
1883 send_dir(path);
1884
1885 if (mc_stat(path, &info) != 0)
1886 send_error();
1887 else if (S_ISLNK(info.st_mode))
1888 printf_send(_("!'%s' is a symbolic link\n"),
1889 g_basename(path));
1890 else
1891 do_chmod(path, NULL);
1892 }
1893
1894 send_done();
1895 }
1896
settype_cb(gpointer data)1897 static void settype_cb(gpointer data)
1898 {
1899 GList *paths = (GList *) data;
1900 int n, i, per;
1901
1902 n=g_list_length(paths);
1903
1904 for (i=0; paths; paths = paths->next, i++)
1905 {
1906 guchar *path = (guchar *) paths->data;
1907 struct stat info;
1908
1909 if(n>1 && i>0)
1910 {
1911 per=100*i/n;
1912 printf_send("%%%d", per);
1913 }
1914 send_dir(path);
1915
1916 if (mc_stat(path, &info) != 0)
1917 send_error();
1918 else if (S_ISLNK(info.st_mode))
1919 printf_send(_("!'%s' is a symbolic link\n"),
1920 g_basename(path));
1921 else
1922 do_settype(path, NULL);
1923 }
1924
1925 send_done();
1926 }
1927
list_cb(gpointer data)1928 static void list_cb(gpointer data)
1929 {
1930 GList *paths = (GList *) data;
1931 int n, i, per;
1932
1933 n=g_list_length(paths);
1934
1935 for (i=0; paths; paths = paths->next, i++)
1936 {
1937 if(n>1 && i>0)
1938 {
1939 per=100*i/n;
1940 printf_send("%%%d", per);
1941 }
1942 send_dir((char *) paths->data);
1943
1944 action_do_func((char *) paths->data, action_dest);
1945 }
1946
1947 send_done();
1948 }
1949
1950 /* EXTERNAL INTERFACE */
1951
action_find(GList * paths)1952 void action_find(GList *paths)
1953 {
1954 GUIside *gui_side;
1955 GtkWidget *abox;
1956
1957 if (!paths)
1958 {
1959 report_error(_("You need to select some items "
1960 "to search through"));
1961 return;
1962 }
1963
1964 if (!last_find_string)
1965 last_find_string = g_strdup("'core'");
1966
1967 new_entry_string = last_find_string;
1968
1969 abox = abox_new(_("Find"), FALSE);
1970 gui_side = start_action(abox, find_cb, paths,
1971 o_action_force.int_value,
1972 o_action_brief.int_value,
1973 o_action_recurse.int_value,
1974 o_action_newer.int_value);
1975 if (!gui_side)
1976 return;
1977
1978 abox_add_results(ABOX(abox));
1979
1980 gui_side->default_string = &last_find_string;
1981 abox_add_entry(ABOX(abox), last_find_string,
1982 new_help_button(show_condition_help, NULL));
1983 g_signal_connect(ABOX(abox)->entry, "changed",
1984 G_CALLBACK(entry_changed), gui_side);
1985 set_find_string_colour(ABOX(abox)->entry, last_find_string);
1986
1987 gui_side->show_info = TRUE;
1988 gui_side->entry_string_func = set_find_string_colour;
1989
1990 number_of_windows++;
1991 gtk_widget_show(abox);
1992 }
1993
1994 /* Count disk space used by selected items */
action_usage(GList * paths)1995 void action_usage(GList *paths)
1996 {
1997 GUIside *gui_side;
1998 GtkWidget *abox;
1999
2000 if (!paths)
2001 {
2002 report_error(_("You need to select some items to count"));
2003 return;
2004 }
2005
2006 abox = abox_new(_("Disk Usage"), TRUE);
2007 if(paths && paths->next)
2008 abox_set_percentage(ABOX(abox), 0);
2009
2010 gui_side = start_action(abox, usage_cb, paths,
2011 o_action_force.int_value,
2012 o_action_brief.int_value,
2013 o_action_recurse.int_value,
2014 o_action_newer.int_value);
2015 if (!gui_side)
2016 return;
2017
2018 gui_side->show_info = TRUE;
2019
2020 number_of_windows++;
2021
2022 gtk_widget_show(abox);
2023 }
2024
2025 /* Mount/unmount listed items (paths).
2026 * Free the list after this function returns.
2027 * If open_dir is TRUE and the dir is successfully mounted, open it.
2028 * quiet can be -1 for default.
2029 */
action_mount(GList * paths,gboolean open_dir,gboolean mount,int quiet)2030 void action_mount(GList *paths, gboolean open_dir, gboolean mount, int quiet)
2031 {
2032 #ifdef DO_MOUNT_POINTS
2033 GUIside *gui_side;
2034 GtkWidget *abox;
2035
2036 if (quiet == -1)
2037 quiet = o_action_mount.int_value;
2038
2039 mount_open_dir = open_dir;
2040 mount_mount = mount;
2041
2042 abox = abox_new(_("Mount / Unmount"), quiet);
2043 if(paths && paths->next)
2044 abox_set_percentage(ABOX(abox), 0);
2045 gui_side = start_action(abox, mount_cb, paths,
2046 o_action_force.int_value,
2047 o_action_brief.int_value,
2048 o_action_recurse.int_value,
2049 o_action_newer.int_value);
2050 if (!gui_side)
2051 return;
2052
2053 log_info_paths("Mount", paths, NULL);
2054
2055 number_of_windows++;
2056 gtk_widget_show(abox);
2057 #else
2058 report_error(
2059 _("ROX-Filer does not yet support mount points on your "
2060 "system. Sorry."));
2061 #endif /* DO_MOUNT_POINTS */
2062 }
2063
2064 /* Delete these paths */
action_delete(GList * paths)2065 void action_delete(GList *paths)
2066 {
2067 GUIside *gui_side;
2068 GtkWidget *abox;
2069
2070 if (!remove_pinned_ok(paths))
2071 return;
2072
2073 abox = abox_new(_("Delete"), o_action_delete.int_value);
2074 if(paths && paths->next)
2075 abox_set_percentage(ABOX(abox), 0);
2076 gui_side = start_action(abox, delete_cb, paths,
2077 o_action_force.int_value,
2078 o_action_brief.int_value,
2079 o_action_recurse.int_value,
2080 o_action_newer.int_value);
2081 if (!gui_side)
2082 return;
2083
2084 abox_add_flag(ABOX(abox),
2085 _("Force"), _("Don't confirm deletion of non-writeable items"),
2086 'F', o_action_force.int_value);
2087 abox_add_flag(ABOX(abox),
2088 _("Brief"), _("Only log directories being deleted"),
2089 'B', o_action_brief.int_value);
2090
2091 log_info_paths("Delete", paths, NULL);
2092
2093 number_of_windows++;
2094 gtk_widget_show(abox);
2095 }
2096
2097 /* Change the permissions of the selected items */
action_chmod(GList * paths,gboolean force_recurse,const char * action)2098 void action_chmod(GList *paths, gboolean force_recurse, const char *action)
2099 {
2100 GtkWidget *abox;
2101 GUIside *gui_side;
2102 static GList *presets = NULL;
2103 gboolean recurse = force_recurse || o_action_recurse.int_value;
2104
2105 if (!paths)
2106 {
2107 report_error(_("You need to select the items "
2108 "whose permissions you want to change"));
2109 return;
2110 }
2111
2112 if (!presets)
2113 {
2114 presets = g_list_append(presets, (gchar *)
2115 _("a+x (Make executable/searchable)"));
2116 presets = g_list_append(presets, (gchar *)
2117 _("a-x (Make non-executable/non-searchable)"));
2118 presets = g_list_append(presets, (gchar *)
2119 _("u+rw (Give owner read+write)"));
2120 presets = g_list_append(presets, (gchar *)
2121 _("go-rwx (Private - owner access only)"));
2122 presets = g_list_append(presets, (gchar *)
2123 _("go=u-w (Public access, not write)"));
2124 }
2125
2126 if (!last_chmod_string)
2127 last_chmod_string = g_strdup((guchar *) presets->data);
2128
2129 if (action)
2130 new_entry_string = g_strdup(action);
2131 else
2132 new_entry_string = g_strdup(last_chmod_string);
2133
2134 abox = abox_new(_("Permissions"), FALSE);
2135 if(paths && paths->next)
2136 abox_set_percentage(ABOX(abox), 0);
2137 gui_side = start_action(abox, chmod_cb, paths,
2138 o_action_force.int_value,
2139 o_action_brief.int_value,
2140 recurse,
2141 o_action_newer.int_value);
2142
2143 if (!gui_side)
2144 goto out;
2145
2146 abox_add_flag(ABOX(abox),
2147 _("Brief"), _("Don't list processed files"),
2148 'B', o_action_brief.int_value);
2149 abox_add_flag(ABOX(abox),
2150 _("Recurse"), _("Also change contents of subdirectories"),
2151 'R', recurse);
2152
2153 gui_side->default_string = &last_chmod_string;
2154 abox_add_combo(ABOX(abox), _("Command:"), presets, new_entry_string,
2155 new_help_button(show_chmod_help, NULL));
2156
2157 g_signal_connect(ABOX(abox)->entry, "changed",
2158 G_CALLBACK(entry_changed), gui_side);
2159 #if 0
2160 g_signal_connect_swapped(gui_side->entry, "activate",
2161 G_CALLBACK(gtk_button_clicked),
2162 gui_side->yes);
2163 #endif
2164
2165 log_info_paths("Change permissions", paths, NULL);
2166
2167 number_of_windows++;
2168 gtk_widget_show(abox);
2169
2170 out:
2171 null_g_free(&new_entry_string);
2172 }
2173
2174 /* Set the MIME type of the selected items */
action_settype(GList * paths,gboolean force_recurse,const char * oldtype)2175 void action_settype(GList *paths, gboolean force_recurse, const char *oldtype)
2176 {
2177 GtkWidget *abox;
2178 GUIside *gui_side;
2179 GList *presets = NULL;
2180 gboolean recurse = force_recurse || o_action_recurse.int_value;
2181
2182 if (!paths)
2183 {
2184 report_error(_("You need to select the items "
2185 "whose type you want to change"));
2186 return;
2187 }
2188
2189 if (!last_settype_string)
2190 last_settype_string = g_strdup("text/plain");
2191
2192 if (oldtype)
2193 new_entry_string = g_strdup(oldtype);
2194 else
2195 new_entry_string = g_strdup(last_settype_string);
2196
2197 abox = abox_new(_("Set type"), FALSE);
2198 if(paths && paths->next)
2199 abox_set_percentage(ABOX(abox), 0);
2200 gui_side = start_action(abox, settype_cb, paths,
2201 o_action_force.int_value,
2202 o_action_brief.int_value,
2203 recurse,
2204 o_action_newer.int_value);
2205
2206 if (!gui_side)
2207 goto out;
2208
2209 abox_add_flag(ABOX(abox),
2210 _("Brief"), _("Don't list processed files"),
2211 'B', o_action_brief.int_value);
2212 abox_add_flag(ABOX(abox),
2213 _("Recurse"), _("Change contents of subdirectories"),
2214 'R', recurse);
2215
2216 gui_side->default_string = &last_settype_string;
2217
2218 /* Note: get the list again each time -- it can change */
2219 presets = mime_type_name_list(TRUE);
2220 abox_add_combo(ABOX(abox), _("Type:"), presets, new_entry_string,
2221 new_help_button(show_settype_help, NULL));
2222 g_list_free(presets);
2223
2224 g_signal_connect(ABOX(abox)->entry, "changed",
2225 G_CALLBACK(entry_changed), gui_side);
2226
2227 log_info_paths("Set file type", paths, NULL);
2228
2229 number_of_windows++;
2230 gtk_widget_show(abox);
2231
2232 out:
2233 null_g_free(&new_entry_string);
2234 }
2235
log_info_paths_leaf(const gchar * message,GList * paths,const gchar * dest,const char * leaf)2236 static void log_info_paths_leaf(const gchar *message, GList *paths,
2237 const gchar *dest, const char *leaf)
2238 {
2239 if (leaf == NULL)
2240 {
2241 log_info_paths(message, paths, dest);
2242 }
2243 else
2244 {
2245 char *new_dest;
2246 new_dest = g_build_filename(dest, leaf, NULL);
2247 log_info_paths(message, paths, new_dest);
2248 g_free(new_dest);
2249 }
2250 }
2251
2252 /* If leaf is NULL then the copy has the same name as the original.
2253 * quiet can be -1 for default.
2254 */
action_copy(GList * paths,const char * dest,const char * leaf,int quiet)2255 void action_copy(GList *paths, const char *dest, const char *leaf, int quiet)
2256 {
2257 GUIside *gui_side;
2258 GtkWidget *abox;
2259
2260 if (quiet == -1)
2261 quiet = o_action_copy.int_value;
2262
2263 action_dest = dest;
2264 action_leaf = leaf;
2265 action_do_func = do_copy;
2266
2267 abox = abox_new(_("Copy"), quiet);
2268 if(paths && paths->next)
2269 abox_set_percentage(ABOX(abox), 0);
2270 gui_side = start_action(abox, list_cb, paths,
2271 o_action_force.int_value,
2272 o_action_brief.int_value,
2273 o_action_recurse.int_value,
2274 o_action_newer.int_value);
2275 if (!gui_side)
2276 return;
2277
2278 abox_add_flag(ABOX(abox),
2279 _("Newer"),
2280 _("Only over-write if source is newer than destination."),
2281 'W', o_action_newer.int_value);
2282 abox_add_flag(ABOX(abox),
2283 _("Brief"), _("Only log directories as they are copied"),
2284 'B', o_action_brief.int_value);
2285
2286 log_info_paths_leaf("Copy", paths, dest, leaf);
2287
2288 number_of_windows++;
2289 gtk_widget_show(abox);
2290 }
2291
2292 /* If leaf is NULL then the file is not renamed.
2293 * quiet can be -1 for default.
2294 */
action_move(GList * paths,const char * dest,const char * leaf,int quiet)2295 void action_move(GList *paths, const char *dest, const char *leaf, int quiet)
2296 {
2297 GUIside *gui_side;
2298 GtkWidget *abox;
2299
2300 if (quiet == -1)
2301 quiet = o_action_move.int_value;
2302
2303 action_dest = dest;
2304 action_leaf = leaf;
2305 action_do_func = do_move;
2306
2307 abox = abox_new(_("Move"), quiet);
2308 if(paths && paths->next)
2309 abox_set_percentage(ABOX(abox), 0);
2310 gui_side = start_action(abox, list_cb, paths,
2311 o_action_force.int_value,
2312 o_action_brief.int_value,
2313 o_action_recurse.int_value,
2314 o_action_newer.int_value);
2315 if (!gui_side)
2316 return;
2317
2318 abox_add_flag(ABOX(abox),
2319 _("Newer"),
2320 _("Only over-write if source is newer than destination."),
2321 'W', o_action_newer.int_value);
2322 abox_add_flag(ABOX(abox),
2323 _("Brief"), _("Don't log each file as it is moved"),
2324 'B', o_action_brief.int_value);
2325
2326 log_info_paths_leaf("Move", paths, dest, leaf);
2327
2328 number_of_windows++;
2329 gtk_widget_show(abox);
2330 }
2331
2332 /* If leaf is NULL then the link will have the same name */
action_link(GList * paths,const char * dest,const char * leaf,gboolean relative)2333 void action_link(GList *paths, const char *dest, const char *leaf,
2334 gboolean relative)
2335 {
2336 GtkWidget *abox;
2337 GUIside *gui_side;
2338
2339 action_dest = dest;
2340 action_leaf = leaf;
2341 if (relative)
2342 action_do_func = do_link_relative;
2343 else
2344 action_do_func = do_link_absolute;
2345
2346 abox = abox_new(_("Link"), o_action_link.int_value);
2347 if(paths && paths->next)
2348 abox_set_percentage(ABOX(abox), 0);
2349 gui_side = start_action(abox, list_cb, paths,
2350 o_action_force.int_value,
2351 o_action_brief.int_value,
2352 o_action_recurse.int_value,
2353 o_action_newer.int_value);
2354 if (!gui_side)
2355 return;
2356
2357 log_info_paths_leaf("Link", paths, dest, leaf);
2358
2359 number_of_windows++;
2360 gtk_widget_show(abox);
2361 }
2362
2363 /* Eject these paths */
action_eject(GList * paths)2364 void action_eject(GList *paths)
2365 {
2366 GUIside *gui_side;
2367 GtkWidget *abox;
2368
2369 abox = abox_new(_("Eject"), TRUE);
2370 if(paths && paths->next)
2371 abox_set_percentage(ABOX(abox), 0);
2372 gui_side = start_action(abox, eject_cb, paths,
2373 o_action_force.int_value,
2374 o_action_brief.int_value,
2375 o_action_recurse.int_value,
2376 o_action_newer.int_value);
2377 if (!gui_side)
2378 return;
2379
2380 log_info_paths("Eject", paths, NULL);
2381
2382 number_of_windows++;
2383 gtk_widget_show(abox);
2384 }
2385
action_init(void)2386 void action_init(void)
2387 {
2388 option_add_int(&o_action_copy, "action_copy", 1);
2389 option_add_int(&o_action_move, "action_move", 1);
2390 option_add_int(&o_action_link, "action_link", 1);
2391 option_add_int(&o_action_delete, "action_delete", 0);
2392 option_add_int(&o_action_mount, "action_mount", 1);
2393 option_add_int(&o_action_force, "action_force", FALSE);
2394 option_add_int(&o_action_brief, "action_brief", FALSE);
2395 option_add_int(&o_action_recurse, "action_recurse", FALSE);
2396 option_add_int(&o_action_newer, "action_newer", FALSE);
2397
2398 option_add_string(&o_action_mount_command,
2399 "action_mount_command", "mount");
2400 option_add_string(&o_action_umount_command,
2401 "action_umount_command", "umount");
2402 option_add_string(&o_action_eject_command,
2403 "action_eject_command", "eject");
2404 }
2405
2406 #define MAX_ASK 4
2407
2408 /* Check to see if any of the selected items (or their children) are
2409 * on the pinboard or panel. If so, ask for confirmation.
2410 *
2411 * TRUE if it's OK to lose them.
2412 */
remove_pinned_ok(GList * paths)2413 static gboolean remove_pinned_ok(GList *paths)
2414 {
2415 GList *ask = NULL, *next;
2416 GString *message;
2417 int i, ask_n = 0;
2418 gboolean retval;
2419
2420 for (; paths; paths = paths->next)
2421 {
2422 guchar *path = (guchar *) paths->data;
2423
2424 if (icons_require(path))
2425 {
2426 if (++ask_n > MAX_ASK)
2427 break;
2428 ask = g_list_append(ask, path);
2429 }
2430 }
2431
2432 if (!ask)
2433 return TRUE;
2434
2435 if (ask_n > MAX_ASK)
2436 {
2437 message = g_string_new(_("Deleting items such as "));
2438 ask_n--;
2439 }
2440 else if (ask_n == 1)
2441 message = g_string_new(_("Deleting the item "));
2442 else
2443 message = g_string_new(_("Deleting the items "));
2444
2445 i = 0;
2446 for (next = ask; next; next = next->next)
2447 {
2448 guchar *path = (guchar *) next->data;
2449 guchar *leaf;
2450
2451 leaf = strrchr(path, '/');
2452 if (leaf)
2453 leaf++;
2454 else
2455 leaf = path;
2456
2457 g_string_append_c(message, '`');
2458 g_string_append(message, leaf);
2459 g_string_append_c(message, '\'');
2460 i++;
2461 if (i == ask_n - 1 && i > 0)
2462 g_string_append(message, _(" and "));
2463 else if (i < ask_n)
2464 g_string_append(message, ", ");
2465 }
2466
2467 g_list_free(ask);
2468
2469 if (ask_n == 1)
2470 message = g_string_append(message,
2471 _(" will affect some items on the pinboard "
2472 "or panel - really delete it?"));
2473 else
2474 {
2475 if (ask_n > MAX_ASK)
2476 message = g_string_append_c(message, ',');
2477 message = g_string_append(message,
2478 _(" will affect some items on the pinboard "
2479 "or panel - really delete them?"));
2480 }
2481
2482 retval = confirm(message->str, GTK_STOCK_DELETE, NULL);
2483
2484 g_string_free(message, TRUE);
2485
2486 return retval;
2487 }
2488
set_find_string_colour(GtkWidget * widget,const guchar * string)2489 void set_find_string_colour(GtkWidget *widget, const guchar *string)
2490 {
2491 FindCondition *cond;
2492
2493 cond = find_compile(string);
2494 entry_set_error(widget, !cond);
2495
2496 find_condition_free(cond);
2497 }
2498