From 510a6bac6e632fb8f22939f9650804a94ab6b89d Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sat, 30 Nov 2013 12:51:25 +0530 Subject: [PATCH] Handle file renames that only change case on case-insensitive file-systems --- src/calibre/ebooks/oeb/polish/container.py | 7 ++++--- src/calibre/ebooks/oeb/polish/replace.py | 3 +++ src/calibre/ebooks/oeb/polish/tests/container.py | 10 +++++++++- 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/calibre/ebooks/oeb/polish/container.py b/src/calibre/ebooks/oeb/polish/container.py index 8944c6ab17..dca3150e1a 100644 --- a/src/calibre/ebooks/oeb/polish/container.py +++ b/src/calibre/ebooks/oeb/polish/container.py @@ -214,12 +214,13 @@ class Container(object): # {{{ should be done once, in bulk. ''' if current_name in self.names_that_must_not_be_changed: raise ValueError('Renaming of %s is not allowed' % current_name) - if self.exists(new_name): - raise ValueError('Cannot rename %s to %s as %s already exists' % (self.opf_name, new_name, new_name)) + if self.exists(new_name) and (new_name == current_name or new_name.lower() != current_name.lower()): + # The destination exists and does not differ from the current name only by case + raise ValueError('Cannot rename %s to %s as %s already exists' % (current_name, new_name, new_name)) new_path = self.name_to_abspath(new_name) base = os.path.dirname(new_path) if os.path.isfile(base): - raise ValueError('Cannot rename %s to %s as %s is a file' % (self.opf_name, new_name, base)) + raise ValueError('Cannot rename %s to %s as %s is a file' % (current_name, new_name, base)) if not os.path.exists(base): os.makedirs(base) old_path = parent_dir = self.name_to_abspath(current_name) diff --git a/src/calibre/ebooks/oeb/polish/replace.py b/src/calibre/ebooks/oeb/polish/replace.py index 7945787dd3..8b747686fe 100644 --- a/src/calibre/ebooks/oeb/polish/replace.py +++ b/src/calibre/ebooks/oeb/polish/replace.py @@ -109,6 +109,9 @@ def rename_files(container, file_map): raise ValueError('Circular rename detected. The files %s are both rename targets and destinations' % ', '.join(overlap)) for name, dest in file_map.iteritems(): if container.exists(dest): + if name != dest and name.lower() == dest.lower(): + # A case change on an OS with a case insensitive file-system. + continue raise ValueError('Cannot rename {0} to {1} as {1} already exists'.format(name, dest)) if len(tuple(file_map.itervalues())) != len(set(file_map.itervalues())): raise ValueError('Cannot rename, the set of destination files contains duplicates') diff --git a/src/calibre/ebooks/oeb/polish/tests/container.py b/src/calibre/ebooks/oeb/polish/tests/container.py index e86b9b04bd..2a3965944b 100644 --- a/src/calibre/ebooks/oeb/polish/tests/container.py +++ b/src/calibre/ebooks/oeb/polish/tests/container.py @@ -149,7 +149,10 @@ class ContainerTests(BaseTest): # Test renaming of font files c = new_container() - rename_files(c, {'LiberationMono-Regular.ttf': 'fonts/LiberationMono Regular.ttf'}) + fname = 'LiberationMono-Regular.ttf' + if fname not in c.name_path_map: + fname = fname.lower() # On OS X the font file name is lowercased for some reason (maybe on windows too) + rename_files(c, {fname: 'fonts/LiberationMono Regular.ttf'}) self.check_links(c) # Test renaming of text files @@ -157,6 +160,11 @@ class ContainerTests(BaseTest): rename_files(c, {'index_split_000.html':'text/page one fällen.html', 'index_split_001.html':'text/page two fällen.html'}) self.check_links(c) + # Test rename with only case change + c = new_container() + rename_files(c, {'index_split_000.html':'Index_split_000.html'}) + self.check_links(c) + # self.run_external_tools(c, gvim=True) def test_file_add(self):