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>&lt;, &lt;=, =, !=, &gt;, &gt;=, 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