diff --git a/src/calibre/utils/filenames.py b/src/calibre/utils/filenames.py index 6d18173a8f..afb3f21aae 100644 --- a/src/calibre/utils/filenames.py +++ b/src/calibre/utils/filenames.py @@ -3,12 +3,16 @@ Make strings safe for use as ASCII filenames, while trying to preserve as much meaning as possible. ''' -import os, errno, time, shutil +import errno +import os +import shutil +import time from math import ceil -from calibre import sanitize_file_name, isbytestring, force_unicode, prints -from calibre.constants import (preferred_encoding, iswindows, - filesystem_encoding) +from calibre import force_unicode, isbytestring, prints, sanitize_file_name +from calibre.constants import ( + filesystem_encoding, iswindows, plugins, preferred_encoding +) from calibre.utils.localization import get_udc @@ -476,15 +480,30 @@ def nlinks_file(path): return os.stat(path).st_nlink +if iswindows: + def rename_file(a, b): + move_file = getattr(plugins['winutil'][0], 'move_file', None) + if move_file is None: + import win32file + + def mf_impl(a, b): + win32file.MoveFileEx(a, b, win32file.MOVEFILE_REPLACE_EXISTING|win32file.MOVEFILE_WRITE_THROUGH) + move_file = mf_impl + if isinstance(a, bytes): + a = a.decode('mbcs') + if isinstance(b, bytes): + b = b.decode('mbcs') + move_file(a, b) + + def atomic_rename(oldpath, newpath): '''Replace the file newpath with the file oldpath. Can fail if the files are on different volumes. If succeeds, guaranteed to be atomic. newpath may or may not exist. If it exists, it is replaced. ''' if iswindows: - import win32file for i in xrange(10): try: - win32file.MoveFileEx(oldpath, newpath, win32file.MOVEFILE_REPLACE_EXISTING|win32file.MOVEFILE_WRITE_THROUGH) + rename_file(oldpath, newpath) break except Exception: if i > 8: diff --git a/src/calibre/utils/windows/winutil.c b/src/calibre/utils/windows/winutil.c index bcad9f0c60..a1ed8e11ec 100644 --- a/src/calibre/utils/windows/winutil.c +++ b/src/calibre/utils/windows/winutil.c @@ -221,7 +221,7 @@ static PyObject * winutil_get_max_stdio(PyObject *self, PyObject *args) { return Py_BuildValue("i", _getmaxstdio()); } - + static PyObject * winutil_set_max_stdio(PyObject *self, PyObject *args) { int num = 0; @@ -229,7 +229,7 @@ winutil_set_max_stdio(PyObject *self, PyObject *args) { if (_setmaxstdio(num) == -1) return PyErr_SetFromErrno(PyExc_ValueError); Py_RETURN_NONE; } - + static PyObject * winutil_getenv(PyObject *self, PyObject *args) { const wchar_t *q; @@ -239,6 +239,15 @@ winutil_getenv(PyObject *self, PyObject *args) { return PyUnicode_FromWideChar(ans, wcslen(ans)); } +static PyObject* +winutil_move_file(PyObject *self, PyObject *args) { + Py_UNICODE *a, *b; + unsigned int flags = MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH; + if (!PyArg_ParseTuple(args, "uu|I", &a, &b, &flags)) return NULL; + if (!MoveFileExW(a, b, flags)) { PyErr_SetFromWindowsErr(0); return NULL; } + Py_RETURN_NONE; +} + static PyObject * winutil_username(PyObject *self) { wchar_t buf[UNLEN + 1] = {0}; @@ -278,8 +287,8 @@ winutil_localeconv(PyObject *self) { struct lconv *d = localeconv(); #define W(name) #name, d->_W_##name return Py_BuildValue( - "{su su su su su su su su}", - W(decimal_point), W(thousands_sep), W(int_curr_symbol), W(currency_symbol), + "{su su su su su su su su}", + W(decimal_point), W(thousands_sep), W(int_curr_symbol), W(currency_symbol), W(mon_decimal_point), W(mon_thousands_sep), W(positive_sign), W(negative_sign) ); #undef W @@ -459,6 +468,10 @@ be a unicode string. Returns unicode strings." "localeconv()\n\nGet the locale conventions as unicode strings." }, + {"move_file", (PyCFunction)winutil_move_file, METH_VARARGS, + "move_file()\n\nRename the specified file." + }, + {NULL, NULL, 0, NULL} }; @@ -491,4 +504,3 @@ initwinutil(void) { PyModule_AddIntConstant(m, "CSIDL_PROFILE", CSIDL_PROFILE); } -