nautilus-file-operations.c 156 KB
Newer Older
Ettore Perazzoli's avatar
Ettore Perazzoli committed
1
/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
2

3
/* nautilus-file-operations.c - Nautilus file operations.
Ettore Perazzoli's avatar
Ettore Perazzoli committed
4

Elliot Lee's avatar
Elliot Lee committed
5
   Copyright (C) 1999, 2000 Free Software Foundation
6
   Copyright (C) 2000, 2001 Eazel, Inc.
Alexander Larsson's avatar
Alexander Larsson committed
7
   Copyright (C) 2007 Red Hat, Inc.
Ettore Perazzoli's avatar
Ettore Perazzoli committed
8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23

   This program is free software; you can redistribute it and/or
   modify it under the terms of the GNU General Public License as
   published by the Free Software Foundation; either version 2 of the
   License, or (at your option) any later version.
   
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   General Public License for more details.
   
   You should have received a copy of the GNU General Public
   License along with this program; if not, write to the
   Free Software Foundation, Inc., 59 Temple Place - Suite 330,
   Boston, MA 02111-1307, USA.
   
Alexander Larsson's avatar
Alexander Larsson committed
24 25
   Authors: Alexander Larsson <alexl@redhat.com>
            Ettore Perazzoli <ettore@gnu.org> 
26
            Pavel Cisler <pavel@eazel.com> 
27
 */
Ettore Perazzoli's avatar
Ettore Perazzoli committed
28 29

#include <config.h>
30
#include <string.h>
31
#include <stdio.h>
Alexander Larsson's avatar
Alexander Larsson committed
32
#include <stdarg.h>
33
#include <locale.h>
Alexander Larsson's avatar
Alexander Larsson committed
34
#include <math.h>
35 36 37 38
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>

39
#include "nautilus-file-operations.h"
Ettore Perazzoli's avatar
Ettore Perazzoli committed
40

Alexander Larsson's avatar
Alexander Larsson committed
41
#include "nautilus-file-changes-queue.h"
42
#include "nautilus-lib-self-check-functions.h"
Ramiro Estrugo's avatar
Ramiro Estrugo committed
43

Alexander Larsson's avatar
Alexander Larsson committed
44 45
#include "nautilus-progress-info.h"

Ramiro Estrugo's avatar
Ramiro Estrugo committed
46 47 48
#include <eel/eel-glib-extensions.h>
#include <eel/eel-gtk-extensions.h>
#include <eel/eel-stock-dialogs.h>
Ramiro Estrugo's avatar
Ramiro Estrugo committed
49 50
#include <eel/eel-vfs-extensions.h>

51
#include <glib/gi18n.h>
52
#include <glib/gstdio.h>
53
#include <gdk/gdk.h>
54
#include <gtk/gtk.h>
55
#include <gio/gio.h>
56
#include <glib.h>
57
#include "nautilus-file-changes-queue.h"
Alexander Larsson's avatar
Alexander Larsson committed
58 59 60
#include "nautilus-file-private.h"
#include "nautilus-desktop-icon-file.h"
#include "nautilus-desktop-link-monitor.h"
Ramiro Estrugo's avatar
Ramiro Estrugo committed
61 62 63
#include "nautilus-global-preferences.h"
#include "nautilus-link.h"
#include "nautilus-trash-monitor.h"
64
#include "nautilus-file-utilities.h"
65
#include "nautilus-file-conflict-dialog.h"
Ettore Perazzoli's avatar
Ettore Perazzoli committed
66

67
/* TODO: TESTING!!! */
Alexander Larsson's avatar
Alexander Larsson committed
68 69

typedef struct {
70
	GIOSchedulerJob *io_job;	
Alexander Larsson's avatar
Alexander Larsson committed
71
	GTimer *time;
72 73
	GtkWindow *parent_window;
	int screen_num;
74
	int inhibit_cookie;
Alexander Larsson's avatar
Alexander Larsson committed
75 76 77 78 79 80 81 82
	NautilusProgressInfo *progress;
	GCancellable *cancellable;
	GHashTable *skip_files;
	GHashTable *skip_readdir_error;
	gboolean skip_all_error;
	gboolean skip_all_conflict;
	gboolean merge_all;
	gboolean replace_all;
83
	gboolean delete_all;
Alexander Larsson's avatar
Alexander Larsson committed
84 85 86 87 88 89 90
} CommonJob;

typedef struct {
	CommonJob common;
	gboolean is_move;
	GList *files;
	GFile *destination;
91
	GFile *desktop_location;
92
	GFile *fake_display_source;
Alexander Larsson's avatar
Alexander Larsson committed
93 94 95
	GdkPoint *icon_positions;
	int n_icon_positions;
	GHashTable *debuting_files;
96
	gchar *target_name;
Alexander Larsson's avatar
Alexander Larsson committed
97 98 99 100
	NautilusCopyCallback  done_callback;
	gpointer done_callback_data;
} CopyMoveJob;

101 102 103 104
typedef struct {
	CommonJob common;
	GList *files;
	gboolean try_trash;
105
	gboolean user_cancel;
106 107 108 109
	NautilusDeleteCallback done_callback;
	gpointer done_callback_data;
} DeleteJob;

110 111 112 113 114 115 116
typedef struct {
	CommonJob common;
	GFile *dest_dir;
	char *filename;
	gboolean make_dir;
	GFile *src;
	char *src_data;
James Dietrich's avatar
James Dietrich committed
117
	int length;
118 119 120 121 122 123 124 125
	GdkPoint position;
	gboolean has_position;
	GFile *created_file;
	NautilusCreateCallback done_callback;
	gpointer done_callback_data;
} CreateJob;


Alexander Larsson's avatar
Alexander Larsson committed
126 127
typedef struct {
	CommonJob common;
128
	GList *trash_dirs;
129
	gboolean should_confirm;
130 131
	NautilusOpCallback done_callback;
	gpointer done_callback_data;
Alexander Larsson's avatar
Alexander Larsson committed
132
} EmptyTrashJob;
133

134 135 136
typedef struct {
	CommonJob common;
	GFile *file;
137
	gboolean interactive;
138 139 140 141
	NautilusOpCallback done_callback;
	gpointer done_callback_data;
} MarkTrustedJob;

142 143 144 145 146 147 148 149 150 151 152
typedef struct {
	CommonJob common;
	GFile *file;
	NautilusOpCallback done_callback;
	gpointer done_callback_data;
	guint32 file_permissions;
	guint32 file_mask;
	guint32 dir_permissions;
	guint32 dir_mask;
} SetPermissionsJob;

153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171
typedef enum {
	OP_KIND_COPY,
	OP_KIND_MOVE,
	OP_KIND_DELETE,
	OP_KIND_TRASH
} OpKind;

typedef struct {
	int num_files;
	goffset num_bytes;
	int num_files_since_progress;
	OpKind op;
} SourceInfo;

typedef struct {
	int num_files;
	goffset num_bytes;
	OpKind op;
	guint64 last_report_time;
172
	int last_reported_files_left;
173 174
} TransferInfo;

Alexander Larsson's avatar
Alexander Larsson committed
175 176 177 178
#define SECONDS_NEEDED_FOR_RELIABLE_TRANSFER_RATE 15
#define NSEC_PER_SEC 1000000000
#define NSEC_PER_MSEC 1000000

179 180
#define MAXIMUM_DISPLAYED_FILE_NAME_LENGTH 50

Alexander Larsson's avatar
Alexander Larsson committed
181 182 183 184 185
#define IS_IO_ERROR(__error, KIND) (((__error)->domain == G_IO_ERROR && (__error)->code == G_IO_ERROR_ ## KIND))

#define SKIP _("_Skip")
#define SKIP_ALL _("S_kip All")
#define RETRY _("_Retry")
186
#define DELETE_ALL _("Delete _All")
Alexander Larsson's avatar
Alexander Larsson committed
187 188 189 190
#define REPLACE _("_Replace")
#define REPLACE_ALL _("Replace _All")
#define MERGE _("_Merge")
#define MERGE_ALL _("Merge _All")
191
#define COPY_FORCE _("Copy _Anyway")
Alexander Larsson's avatar
Alexander Larsson committed
192

193 194 195 196 197 198
static void
mark_desktop_file_trusted (CommonJob *common,
			   GCancellable *cancellable,
			   GFile *file,
			   gboolean interactive);

199 200 201 202 203 204 205 206 207 208 209
static gboolean
is_all_button_text (const char *button_text)
{
	g_assert (button_text != NULL);

	return !strcmp (button_text, SKIP_ALL) ||
	       !strcmp (button_text, REPLACE_ALL) ||
	       !strcmp (button_text, DELETE_ALL) ||
	       !strcmp (button_text, MERGE_ALL);
}

210 211 212 213 214 215
static void scan_sources (GList *files,
			  SourceInfo *source_info,
			  CommonJob *job,
			  OpKind kind);


216 217 218
static gboolean empty_trash_job (GIOSchedulerJob *io_job,
				 GCancellable *cancellable,
				 gpointer user_data);
219

220 221 222
static char * query_fs_type (GFile *file,
			     GCancellable *cancellable);

223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247
/* keep in time with format_time()
 *
 * This counts and outputs the number of “time units”
 * formatted and displayed by format_time().
 * For instance, if format_time outputs “3 hours, 4 minutes”
 * it yields 7.
 */
static int
seconds_count_format_time_units (int seconds)
{
	int minutes;
	int hours;

	if (seconds < 0) {
		/* Just to make sure... */
		seconds = 0;
	}

	if (seconds < 60) {
		/* seconds */
		return seconds;
	}

	if (seconds < 60*60) {
		/* minutes */
248
		minutes = seconds / 60;
249 250 251 252 253 254 255
		return minutes;
	}

	hours = seconds / (60*60);

	if (seconds < 60*60*4) {
		/* minutes + hours */
256
		minutes = (seconds - hours * 60 * 60) / 60;
257 258 259 260 261 262
		return minutes + hours;
	}

	return hours;
}

Alexander Larsson's avatar
Alexander Larsson committed
263 264 265 266 267 268 269 270 271 272 273 274 275
static char *
format_time (int seconds)
{
	int minutes;
	int hours;
	char *res;

	if (seconds < 0) {
		/* Just to make sure... */
		seconds = 0;
	}
	
	if (seconds < 60) {
276
		return g_strdup_printf (ngettext ("%'d second","%'d seconds", (int) seconds), (int) seconds);
Alexander Larsson's avatar
Alexander Larsson committed
277 278 279
	}

	if (seconds < 60*60) {
280
		minutes = seconds / 60;
281
		return g_strdup_printf (ngettext ("%'d minute", "%'d minutes", minutes), minutes);
Alexander Larsson's avatar
Alexander Larsson committed
282 283 284 285 286 287 288
	}

	hours = seconds / (60*60);
	
	if (seconds < 60*60*4) {
		char *h, *m;

289
		minutes = (seconds - hours * 60 * 60) / 60;
Alexander Larsson's avatar
Alexander Larsson committed
290
		
291 292
		h = g_strdup_printf (ngettext ("%'d hour", "%'d hours", hours), hours);
		m = g_strdup_printf (ngettext ("%'d minute", "%'d minutes", minutes), minutes);
Alexander Larsson's avatar
Alexander Larsson committed
293 294 295 296 297 298
		res = g_strconcat (h, ", ", m, NULL);
		g_free (h);
		g_free (m);
		return res;
	}
	
299 300
	return g_strdup_printf (ngettext ("approximately %'d hour",
					  "approximately %'d hours",
301
					  hours), hours);
Alexander Larsson's avatar
Alexander Larsson committed
302 303
}

304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341
static char *
shorten_utf8_string (const char *base, int reduce_by_num_bytes)
{
	int len;
	char *ret;
	const char *p;
	
	len = strlen (base);
	len -= reduce_by_num_bytes;
	
	if (len <= 0) {
		return NULL;
	}

	ret = g_new (char, len + 1);

	p = base;
	while (len) {
		char *next;
		next = g_utf8_next_char (p);
		if (next - p > len || *next == '\0') {
			break;
		}
		
		len -= next - p;
		p = next;
	}
	
	if (p - base == 0) {
		g_free (ret);
		return NULL;
	} else {
		memcpy (ret, base, p - base);
		ret[p - base] = '\0';
		return ret;
	}
}

342
/* Note that we have these two separate functions with separate format
343
 * strings for ease of localization.
344 345 346
 */

static char *
347
get_link_name (const char *name, int count, int max_length)
348
{
349
	const char *format;
Alexander Larsson's avatar
Alexander Larsson committed
350
	char *result;
351 352
	int unshortened_length;
	gboolean use_count;
353
	
354 355
	g_assert (name != NULL);

356
	if (count < 0) {
357
		g_warning ("bad count in get_link_name");
358
		count = 0;
359 360
	}

361 362 363
	if (count <= 2) {
		/* Handle special cases for low numbers.
		 * Perhaps for some locales we will need to add more.
364
		 */
365
		switch (count) {
366 367 368
		default:
			g_assert_not_reached ();
			/* fall through */
369 370
		case 0:
			/* duplicate original file name */
371
			format = "%s";
372
			break;
373
		case 1:
374
			/* appended to new link file */
375
			format = _("Link to %s");
376 377
			break;
		case 2:
378
			/* appended to new link file */
379
			format = _("Another link to %s");
380 381 382
			break;
		}

383
		use_count = FALSE;
384 385 386 387
	} else {
		/* Handle special cases for the first few numbers of each ten.
		 * For locales where getting this exactly right is difficult,
		 * these can just be made all the same as the general case below.
388
		 */
389 390 391 392 393 394
		switch (count % 10) {
		case 1:
			/* Localizers: Feel free to leave out the "st" suffix
			 * if there's no way to do that nicely for a
			 * particular language.
			 */
395
			format = _("%'dst link to %s");
396 397
			break;
		case 2:
398
			/* appended to new link file */
399
			format = _("%'dnd link to %s");
400 401
			break;
		case 3:
402
			/* appended to new link file */
403
			format = _("%'drd link to %s");
404 405
			break;
		default:
406
			/* appended to new link file */
407
			format = _("%'dth link to %s");
408 409
			break;
		}
410 411 412 413 414

		use_count = TRUE;
	}

	if (use_count)
Alexander Larsson's avatar
Alexander Larsson committed
415
		result = g_strdup_printf (format, count, name);
416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433
	else
		result = g_strdup_printf (format, name);

	if (max_length > 0 && (unshortened_length = strlen (result)) > max_length) {
		char *new_name;

		new_name = shorten_utf8_string (name, unshortened_length - max_length);
		if (new_name) {
			g_free (result);

			if (use_count)
				result = g_strdup_printf (format, count, new_name);
			else
				result = g_strdup_printf (format, new_name);

			g_assert (strlen (result) <= max_length);
			g_free (new_name);
		}
434
	}
435

436
	return result;
437 438
}

Alexander Larsson's avatar
Alexander Larsson committed
439

440 441 442 443 444
/* Localizers: 
 * Feel free to leave out the st, nd, rd and th suffix or
 * make some or all of them match.
 */

445
/* localizers: tag used to detect the first copy of a file */
446
static const char untranslated_copy_duplicate_tag[] = N_(" (copy)");
447
/* localizers: tag used to detect the second copy of a file */
448
static const char untranslated_another_copy_duplicate_tag[] = N_(" (another copy)");
449 450 451 452 453 454 455 456

/* localizers: tag used to detect the x11th copy of a file */
static const char untranslated_x11th_copy_duplicate_tag[] = N_("th copy)");
/* localizers: tag used to detect the x12th copy of a file */
static const char untranslated_x12th_copy_duplicate_tag[] = N_("th copy)");
/* localizers: tag used to detect the x13th copy of a file */
static const char untranslated_x13th_copy_duplicate_tag[] = N_("th copy)");

457
/* localizers: tag used to detect the x1st copy of a file */
458
static const char untranslated_st_copy_duplicate_tag[] = N_("st copy)");
459
/* localizers: tag used to detect the x2nd copy of a file */
460
static const char untranslated_nd_copy_duplicate_tag[] = N_("nd copy)");
461
/* localizers: tag used to detect the x3rd copy of a file */
462
static const char untranslated_rd_copy_duplicate_tag[] = N_("rd copy)");
463

464
/* localizers: tag used to detect the xxth copy of a file */
465 466 467 468
static const char untranslated_th_copy_duplicate_tag[] = N_("th copy)");

#define COPY_DUPLICATE_TAG _(untranslated_copy_duplicate_tag)
#define ANOTHER_COPY_DUPLICATE_TAG _(untranslated_another_copy_duplicate_tag)
469 470 471 472
#define X11TH_COPY_DUPLICATE_TAG _(untranslated_x11th_copy_duplicate_tag)
#define X12TH_COPY_DUPLICATE_TAG _(untranslated_x12th_copy_duplicate_tag)
#define X13TH_COPY_DUPLICATE_TAG _(untranslated_x13th_copy_duplicate_tag)

473 474 475 476
#define ST_COPY_DUPLICATE_TAG _(untranslated_st_copy_duplicate_tag)
#define ND_COPY_DUPLICATE_TAG _(untranslated_nd_copy_duplicate_tag)
#define RD_COPY_DUPLICATE_TAG _(untranslated_rd_copy_duplicate_tag)
#define TH_COPY_DUPLICATE_TAG _(untranslated_th_copy_duplicate_tag)
477 478

/* localizers: appended to first file copy */
479
static const char untranslated_first_copy_duplicate_format[] = N_("%s (copy)%s");
480
/* localizers: appended to second file copy */
481
static const char untranslated_second_copy_duplicate_format[] = N_("%s (another copy)%s");
482 483

/* localizers: appended to x11th file copy */
484
static const char untranslated_x11th_copy_duplicate_format[] = N_("%s (%'dth copy)%s");
485
/* localizers: appended to x12th file copy */
486
static const char untranslated_x12th_copy_duplicate_format[] = N_("%s (%'dth copy)%s");
487
/* localizers: appended to x13th file copy */
488
static const char untranslated_x13th_copy_duplicate_format[] = N_("%s (%'dth copy)%s");
489

490 491 492 493 494
/* localizers: if in your language there's no difference between 1st, 2nd, 3rd and nth
 * plurals, you can leave the st, nd, rd suffixes out and just make all the translated
 * strings look like "%s (copy %'d)%s".
 */

495
/* localizers: appended to x1st file copy */
496
static const char untranslated_st_copy_duplicate_format[] = N_("%s (%'dst copy)%s");
497
/* localizers: appended to x2nd file copy */
498
static const char untranslated_nd_copy_duplicate_format[] = N_("%s (%'dnd copy)%s");
499
/* localizers: appended to x3rd file copy */
500
static const char untranslated_rd_copy_duplicate_format[] = N_("%s (%'drd copy)%s");
501
/* localizers: appended to xxth file copy */
502
static const char untranslated_th_copy_duplicate_format[] = N_("%s (%'dth copy)%s");
503 504 505

#define FIRST_COPY_DUPLICATE_FORMAT _(untranslated_first_copy_duplicate_format)
#define SECOND_COPY_DUPLICATE_FORMAT _(untranslated_second_copy_duplicate_format)
506 507 508 509
#define X11TH_COPY_DUPLICATE_FORMAT _(untranslated_x11th_copy_duplicate_format)
#define X12TH_COPY_DUPLICATE_FORMAT _(untranslated_x12th_copy_duplicate_format)
#define X13TH_COPY_DUPLICATE_FORMAT _(untranslated_x13th_copy_duplicate_format)

510 511 512 513
#define ST_COPY_DUPLICATE_FORMAT _(untranslated_st_copy_duplicate_format)
#define ND_COPY_DUPLICATE_FORMAT _(untranslated_nd_copy_duplicate_format)
#define RD_COPY_DUPLICATE_FORMAT _(untranslated_rd_copy_duplicate_format)
#define TH_COPY_DUPLICATE_FORMAT _(untranslated_th_copy_duplicate_format)
514 515

static char *
516
extract_string_until (const char *original, const char *until_substring)
517 518 519
{
	char *result;
	
520
	g_assert ((int) strlen (original) >= until_substring - original);
521
	g_assert (until_substring - original >= 0);
522

523 524 525
	result = g_malloc (until_substring - original + 1);
	strncpy (result, original, until_substring - original);
	result[until_substring - original] = '\0';
526 527 528 529
	
	return result;
}

530 531 532 533
/* Dismantle a file name, separating the base name, the file suffix and removing any
 * (xxxcopy), etc. string. Figure out the count that corresponds to the given
 * (xxxcopy) substring.
 */
534
static void
535 536 537 538
parse_previous_duplicate_name (const char *name,
			       char **name_base,
			       const char **suffix,
			       int *count)
539 540
{
	const char *tag;
541 542

	g_assert (name[0] != '\0');
543

544
	*suffix = eel_filename_get_extension_offset (name + 1);
545

546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574
	if (*suffix == NULL || (*suffix)[1] == '\0') {
		/* no suffix */
		*suffix = "";
	}

	tag = strstr (name, COPY_DUPLICATE_TAG);
	if (tag != NULL) {
		if (tag > *suffix) {
			/* handle case "foo. (copy)" */
			*suffix = "";
		}
		*name_base = extract_string_until (name, tag);
		*count = 1;
		return;
	}


	tag = strstr (name, ANOTHER_COPY_DUPLICATE_TAG);
	if (tag != NULL) {
		if (tag > *suffix) {
			/* handle case "foo. (another copy)" */
			*suffix = "";
		}
		*name_base = extract_string_until (name, tag);
		*count = 2;
		return;
	}


575
	/* Check to see if we got one of st, nd, rd, th. */
576 577 578 579 580 581 582 583
	tag = strstr (name, X11TH_COPY_DUPLICATE_TAG);

	if (tag == NULL) {
		tag = strstr (name, X12TH_COPY_DUPLICATE_TAG);
	}
	if (tag == NULL) {
		tag = strstr (name, X13TH_COPY_DUPLICATE_TAG);
	}
584

585 586 587
	if (tag == NULL) {
		tag = strstr (name, ST_COPY_DUPLICATE_TAG);
	}
588 589 590 591 592 593 594 595 596 597
	if (tag == NULL) {
		tag = strstr (name, ND_COPY_DUPLICATE_TAG);
	}
	if (tag == NULL) {
		tag = strstr (name, RD_COPY_DUPLICATE_TAG);
	}
	if (tag == NULL) {
		tag = strstr (name, TH_COPY_DUPLICATE_TAG);
	}

598
	/* If we got one of st, nd, rd, th, fish out the duplicate number. */
599
	if (tag != NULL) {
600
		/* localizers: opening parentheses to match the "th copy)" string */
601
		tag = strstr (name, _(" ("));
602 603 604 605 606 607
		if (tag != NULL) {
			if (tag > *suffix) {
				/* handle case "foo. (22nd copy)" */
				*suffix = "";
			}
			*name_base = extract_string_until (name, tag);
608
			/* localizers: opening parentheses of the "th copy)" string */
609
			if (sscanf (tag, _(" (%'d"), count) == 1) {
610 611 612 613
				if (*count < 1 || *count > 1000000) {
					/* keep the count within a reasonable range */
					*count = 0;
				}
614 615 616 617 618 619 620 621 622 623
				return;
			}
			*count = 0;
			return;
		}
	}

	
	*count = 0;
	if (**suffix != '\0') {
624 625
		*name_base = extract_string_until (name, *suffix);
	} else {
626
		*name_base = g_strdup (name);
627 628 629
	}
}

630
static char *
631
make_next_duplicate_name (const char *base, const char *suffix, int count, int max_length)
632
{
633 634
	const char *format;
	char *result;
635 636
	int unshortened_length;
	gboolean use_count;
637 638

	if (count < 1) {
639
		g_warning ("bad count %d in get_duplicate_name", count);
640 641 642
		count = 1;
	}

643
	if (count <= 2) {
644

645 646 647 648
		/* Handle special cases for low numbers.
		 * Perhaps for some locales we will need to add more.
		 */
		switch (count) {
649 650 651
		default:
			g_assert_not_reached ();
			/* fall through */
652
		case 1:
653
			format = FIRST_COPY_DUPLICATE_FORMAT;
654 655
			break;
		case 2:
656
			format = SECOND_COPY_DUPLICATE_FORMAT;
657
			break;
658

659
		}
660 661

		use_count = FALSE;
662 663 664 665 666 667
	} else {

		/* Handle special cases for the first few numbers of each ten.
		 * For locales where getting this exactly right is difficult,
		 * these can just be made all the same as the general case below.
		 */
668 669 670 671 672 673

		/* Handle special cases for x11th - x20th.
		 */
		switch (count % 100) {
		case 11:
			format = X11TH_COPY_DUPLICATE_FORMAT;
674
			break;
675 676
		case 12:
			format = X12TH_COPY_DUPLICATE_FORMAT;
677
			break;
678 679
		case 13:
			format = X13TH_COPY_DUPLICATE_FORMAT;
680 681
			break;
		default:
682
			format = NULL;
683 684 685
			break;
		}

686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703
		if (format == NULL) {
			switch (count % 10) {
			case 1:
				format = ST_COPY_DUPLICATE_FORMAT;
				break;
			case 2:
				format = ND_COPY_DUPLICATE_FORMAT;
				break;
			case 3:
				format = RD_COPY_DUPLICATE_FORMAT;
				break;
			default:
				/* The general case. */
				format = TH_COPY_DUPLICATE_FORMAT;
				break;
			}
		}

704 705 706 707 708
		use_count = TRUE;

	}

	if (use_count)
709
		result = g_strdup_printf (format, base, count, suffix);
710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727
	else
		result = g_strdup_printf (format, base, suffix);

	if (max_length > 0 && (unshortened_length = strlen (result)) > max_length) {
		char *new_base;

		new_base = shorten_utf8_string (base, unshortened_length - max_length);
		if (new_base) {
			g_free (result);

			if (use_count)
				result = g_strdup_printf (format, new_base, count, suffix);
			else
				result = g_strdup_printf (format, new_base, suffix);

			g_assert (strlen (result) <= max_length);
			g_free (new_base);
		}
728 729
	}

730 731 732 733
	return result;
}

static char *
734
get_duplicate_name (const char *name, int count_increment, int max_length)
735 736 737 738 739 740 741
{
	char *result;
	char *name_base;
	const char *suffix;
	int count;

	parse_previous_duplicate_name (name, &name_base, &suffix, &count);
742
	result = make_next_duplicate_name (name_base, suffix, count + count_increment, max_length);
743 744 745 746 747 748

	g_free (name_base);

	return result;
}

749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770
static gboolean
has_invalid_xml_char (char *str)
{
	gunichar c;

	while (*str != 0) {
		c = g_utf8_get_char (str);
		/* characters XML permits */
		if (!(c == 0x9 ||
		      c == 0xA ||
		      c == 0xD ||
		      (c >= 0x20 && c <= 0xD7FF) ||
		      (c >= 0xE000 && c <= 0xFFFD) ||
		      (c >= 0x10000 && c <= 0x10FFFF))) {
			return TRUE;
		}
		str = g_utf8_next_char (str);
	}
	return FALSE;
}


771 772 773 774
static char *
custom_full_name_to_string (char *format, va_list va)
{
	GFile *file;
775
	
776
	file = va_arg (va, GFile *);
777
	
778
	return g_file_get_parse_name (file);
779 780 781
}

static void
782
custom_full_name_skip (va_list *va)
783
{
784
	(void) va_arg (*va, GFile *);
785 786
}

787 788
static char *
custom_basename_to_string (char *format, va_list va)
789
{
790 791
	GFile *file;
	GFileInfo *info;
792
	char *name, *basename, *tmp;
793

794
	file = va_arg (va, GFile *);
795

796
	info = g_file_query_info (file,
797
				  G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME,
798 799 800
				  0,
				  g_cancellable_get_current (),
				  NULL);
801
	
802 803 804 805 806
	name = NULL;
	if (info) {
		name = g_strdup (g_file_info_get_display_name (info));
		g_object_unref (info);
	}
807
	
808 809 810 811 812 813 814
	if (name == NULL) {
		basename = g_file_get_basename (file);
		if (g_utf8_validate (basename, -1, NULL)) {
			name = basename;
		} else {
			name = g_uri_escape_string (basename, G_URI_RESERVED_CHARS_ALLOWED_IN_PATH, TRUE);
			g_free (basename);
815 816
		}
	}
817 818 819 820 821 822 823

	/* Some chars can't be put in the markup we use for the dialogs... */
	if (has_invalid_xml_char (name)) {
		tmp = name;
		name = g_uri_escape_string (name, G_URI_RESERVED_CHARS_ALLOWED_IN_PATH, TRUE);
		g_free (tmp);
	}
824 825 826 827 828 829 830 831

	/* Finally, if the string is too long, truncate it. */
	if (name != NULL) {
		tmp = name;
		name = eel_str_middle_truncate (tmp, MAXIMUM_DISPLAYED_FILE_NAME_LENGTH);
		g_free (tmp);
	}

832 833
	
	return name;
834 835
}

836 837
static void
custom_basename_skip (va_list *va)
838
{
839
	(void) va_arg (*va, GFile *);
840
}
841 842


843 844 845 846
static char *
custom_size_to_string (char *format, va_list va)
{
	goffset size;
847

848
	size = va_arg (va, goffset);
849
	return g_format_size (size);
850 851
}

852
static void
853
custom_size_skip (va_list *va)
854
{
855
	(void) va_arg (*va, goffset);
856 857
}

858 859
static char *
custom_time_to_string (char *format, va_list va)
860
{
861
	int secs;
862

863 864 865
	secs = va_arg (va, int);
	return format_time (secs);
}
866

867 868 869
static void
custom_time_skip (va_list *va)
{
870
	(void) va_arg (*va, int);
871
}
872

873
static char *
874
custom_mount_to_string (char *format, va_list va)
875
{
876
	GMount *mount;
877

878 879
	mount = va_arg (va, GMount *);
	return g_mount_get_name (mount);
880 881 882
}

static void
883
custom_mount_skip (va_list *va)
884
{
885
	(void) va_arg (*va, GMount *);
886 887 888
}


889 890 891 892 893
static EelPrintfHandler handlers[] = {
	{ 'F', custom_full_name_to_string, custom_full_name_skip },
	{ 'B', custom_basename_to_string, custom_basename_skip },
	{ 'S', custom_size_to_string, custom_size_skip },
	{ 'T', custom_time_to_string, custom_time_skip },
894
	{ 'V', custom_mount_to_string, custom_mount_skip },
895 896
	{ 0 }
};
897 898


899 900 901 902
static char *
f (const char *format, ...) {
	va_list va;
	char *res;
903
	
904 905 906
	va_start (va, format);
	res = eel_strdup_vprintf_with_custom (handlers, format, va);
	va_end (va);
907

908 909
	return res;
}
910

911 912 913 914 915 916 917 918 919 920
#define op_job_new(__type, parent_window) ((__type *)(init_common (sizeof(__type), parent_window)))

static gpointer
init_common (gsize job_size,
	     GtkWindow *parent_window)
{
	CommonJob *common;
	GdkScreen *screen;

	common = g_malloc0 (job_size);
921 922

	if (parent_window) {
923 924
		common->parent_window = parent_window;
		eel_add_weak_pointer (&common->parent_window);
925
	}
926 927 928
	common->progress = nautilus_progress_info_new ();
	common->cancellable = nautilus_progress_info_get_cancellable (common->progress);
	common->time = g_timer_new ();
929
	common->inhibit_cookie = -1;
930 931 932 933 934 935 936 937 938 939 940 941 942 943
	common->screen_num = 0;
	if (parent_window) {
		screen = gtk_widget_get_screen (GTK_WIDGET (parent_window));
		common->screen_num = gdk_screen_get_number (screen);
	}
	
	return common;
}

static void
finalize_common (CommonJob *common)
{
	nautilus_progress_info_finish (common->progress);

944 945 946
	if (common->inhibit_cookie != -1) {
		nautilus_uninhibit_power_manager (common->inhibit_cookie);
	}
947

948 949
	common->inhibit_cookie = -1;
	g_timer_destroy (common->time);
950
	eel_remove_weak_pointer (&common->parent_window);
951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005
	if (common->skip_files) {
		g_hash_table_destroy (common->skip_files);
	}
	if (common->skip_readdir_error) {
		g_hash_table_destroy (common->skip_readdir_error);
	}
	g_object_unref (common->progress);
	g_object_unref (common->cancellable);
	g_free (common);
}

static void
skip_file (CommonJob *common,
	   GFile *file)
{
	if (common->skip_files == NULL) {
		common->skip_files =
			g_hash_table_new_full (g_file_hash, (GEqualFunc)g_file_equal, g_object_unref, NULL);
	}

	g_hash_table_insert (common->skip_files, g_object_ref (file), file);
}

static void
skip_readdir_error (CommonJob *common,
		    GFile *dir)
{
	if (common->skip_readdir_error == NULL) {
		common->skip_readdir_error =
			g_hash_table_new_full (g_file_hash, (GEqualFunc)g_file_equal, g_object_unref, NULL);
	}

	g_hash_table_insert (common->skip_readdir_error, g_object_ref (dir), dir);
}

static gboolean
should_skip_file (CommonJob *common,
		  GFile *file)
{
	if (common->skip_files != NULL) {
		return g_hash_table_lookup (common->skip_files, file) != NULL;
	}
	return FALSE;
}

static gboolean
should_skip_readdir_error (CommonJob *common,
			   GFile *dir)
{
	if (common->skip_readdir_error != NULL) {
		return g_hash_table_lookup (common->skip_readdir_error, dir) != NULL;
	}
	return FALSE;
}

Alexander Larsson's avatar
Alexander Larsson committed
1006 1007 1008
static gboolean
can_delete_without_confirm (GFile *file)
{
1009 1010
	if (g_file_has_uri_scheme (file, "burn") ||
	    g_file_has_uri_scheme (file, "x-nautilus-desktop")) {
Alexander Larsson's avatar
Alexander Larsson committed
1011
		return TRUE;
Alexander Larsson's avatar
Alexander Larsson committed
1012 1013
	}

Alexander Larsson's avatar
Alexander Larsson committed
1014
	return FALSE;
1015 1016
}

Alexander Larsson's avatar
Alexander Larsson committed
1017 1018
static gboolean
can_delete_files_without_confirm (GList *files)
1019
{
Alexander Larsson's avatar
Alexander Larsson committed
1020
	g_assert (files != NULL);
1021

Alexander Larsson's avatar
Alexander Larsson committed
1022 1023 1024 1025
	while (files != NULL) {
		if (!can_delete_without_confirm (files->data)) {
			return FALSE;
		}
Alexander Larsson's avatar
 
Alexander Larsson committed
1026

Alexander Larsson's avatar
Alexander Larsson committed
1027 1028
		files = files->next;
	}
Pavel Cisler's avatar
Pavel Cisler committed
1029

Alexander Larsson's avatar
Alexander Larsson committed
1030 1031
	return TRUE;
}
1032

Alexander Larsson's avatar
Alexander Larsson committed
1033
typedef struct {
1034
	GtkWindow **parent_window;
Alexander Larsson's avatar
Alexander Larsson committed
1035 1036 1037 1038 1039 1040
	gboolean ignore_close_box;
	GtkMessageType message_type;
	const char *primary_text;
	const char *secondary_text;
	const char *details_text;
	const char **button_titles;
1041
	gboolean show_all;
Alexander Larsson's avatar
Alexander Larsson committed
1042 1043 1044
	
	int result;
} RunSimpleDialogData;
1045

1046
static gboolean
Alexander Larsson's avatar
Alexander Larsson committed
1047 1048 1049 1050 1051 1052 1053 1054 1055
do_run_simple_dialog (gpointer _data)
{
	RunSimpleDialogData *data = _data;
	const char *button_title;
        GtkWidget *dialog;
	int result;
	int response_id;

	/* Create the dialog. */
1056 1057 1058 1059 1060 1061 1062 1063 1064 1065
	dialog = gtk_message_dialog_new (*data->parent_window,
					 0,
					 data->message_type,
					 GTK_BUTTONS_NONE,
					 NULL);

	g_object_set (dialog,
		      "text", data->primary_text,
		      "secondary-text", data->secondary_text,
		      NULL);
Alexander Larsson's avatar
Alexander Larsson committed
1066 1067 1068 1069 1070

	for (response_id = 0;
	     data->button_titles[response_id] != NULL;
	     response_id++) {
		button_title = data->button_titles[response_id];
1071 1072 1073 1074
		if (!data->show_all && is_all_button_text (button_title)) {
			continue;
		}

Alexander Larsson's avatar
Alexander Larsson committed
1075 1076 1077
		gtk_dialog_add_button (GTK_DIALOG (dialog), button_title, response_id);
		gtk_dialog_set_default_response (GTK_DIALOG (dialog), response_id);
	}
1078

Alexander Larsson's avatar
Alexander Larsson committed
1079
	if (data->details_text) {
1080 1081
		eel_gtk_message_dialog_set_details_label (GTK_MESSAGE_DIALOG (dialog),
							  data->details_text);
Alexander Larsson's avatar
Alexander Larsson committed
1082 1083 1084 1085 1086 1087 1088 1089 1090
	}
	
	/* Run it. */
        result = gtk_dialog_run (GTK_DIALOG (dialog));
	
	while ((result == GTK_RESPONSE_NONE || result == GTK_RESPONSE_DELETE_EVENT) && data->ignore_close_box) {
		result = gtk_dialog_run