diff options
-rw-r--r-- | Include/pyerrors.h | 25 | ||||
-rw-r--r-- | Lib/test/test_unicode_file.py | 4 | ||||
-rw-r--r-- | Modules/posixmodule.c | 391 | ||||
-rw-r--r-- | Objects/fileobject.c | 96 | ||||
-rw-r--r-- | PC/pyconfig.h | 4 | ||||
-rw-r--r-- | Python/errors.c | 92 |
6 files changed, 568 insertions, 44 deletions
diff --git a/Include/pyerrors.h b/Include/pyerrors.h index 119aa2e..18dee28 100644 --- a/Include/pyerrors.h +++ b/Include/pyerrors.h @@ -81,16 +81,37 @@ PyAPI_DATA(PyObject *) PyExc_FutureWarning; PyAPI_FUNC(int) PyErr_BadArgument(void); PyAPI_FUNC(PyObject *) PyErr_NoMemory(void); PyAPI_FUNC(PyObject *) PyErr_SetFromErrno(PyObject *); +PyAPI_FUNC(PyObject *) PyErr_SetFromErrnoWithFilenameObject( + PyObject *, PyObject *); PyAPI_FUNC(PyObject *) PyErr_SetFromErrnoWithFilename(PyObject *, char *); +#ifdef Py_WIN_WIDE_FILENAMES +PyAPI_FUNC(PyObject *) PyErr_SetFromErrnoWithUnicodeFilename( + PyObject *, Py_UNICODE *); +#endif /* Py_WIN_WIDE_FILENAMES */ + PyAPI_FUNC(PyObject *) PyErr_Format(PyObject *, const char *, ...) Py_GCC_ATTRIBUTE((format(printf, 2, 3))); + #ifdef MS_WINDOWS -PyAPI_FUNC(PyObject *) PyErr_SetFromWindowsErrWithFilename(int, const char *); +PyAPI_FUNC(PyObject *) PyErr_SetFromWindowsErrWithFilenameObject( + int, const char *); +PyAPI_FUNC(PyObject *) PyErr_SetFromWindowsErrWithFilename( + int, const char *); +#ifdef Py_WIN_WIDE_FILENAMES +PyAPI_FUNC(PyObject *) PyErr_SetFromWindowsErrWithUnicodeFilename( + int, const Py_UNICODE *); +#endif /* Py_WIN_WIDE_FILENAMES */ PyAPI_FUNC(PyObject *) PyErr_SetFromWindowsErr(int); +PyAPI_FUNC(PyObject *) PyErr_SetExcFromWindowsErrWithFilenameObject( + PyObject *,int, PyObject *); PyAPI_FUNC(PyObject *) PyErr_SetExcFromWindowsErrWithFilename( PyObject *,int, const char *); +#ifdef Py_WIN_WIDE_FILENAMES +PyAPI_FUNC(PyObject *) PyErr_SetExcFromWindowsErrWithUnicodeFilename( + PyObject *,int, const Py_UNICODE *); +#endif /* Py_WIN_WIDE_FILENAMES */ PyAPI_FUNC(PyObject *) PyErr_SetExcFromWindowsErr(PyObject *, int); -#endif +#endif /* MS_WINDOWS */ /* Export the old function so that the existing API remains available: */ PyAPI_FUNC(void) PyErr_BadInternalCall(void); diff --git a/Lib/test/test_unicode_file.py b/Lib/test/test_unicode_file.py index a0de139..4bafd98 100644 --- a/Lib/test/test_unicode_file.py +++ b/Lib/test/test_unicode_file.py @@ -40,6 +40,10 @@ os.chmod(TESTFN_ENCODED, 0777) os.chmod(TESTFN_UNICODE, 0777) # Test rename +try: + os.unlink(TESTFN_ENCODED + ".new") +except os.error: + pass os.rename(TESTFN_ENCODED, TESTFN_ENCODED + ".new") os.rename(TESTFN_UNICODE+".new", TESTFN_ENCODED) diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index 9ac0eb4..494ca61 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -367,6 +367,15 @@ posix_error_with_filename(char* name) return PyErr_SetFromErrnoWithFilename(PyExc_OSError, name); } +#ifdef Py_WIN_WIDE_FILENAMES +static PyObject * +posix_error_with_unicode_filename(Py_UNICODE* name) +{ + return PyErr_SetFromErrnoWithUnicodeFilename(PyExc_OSError, name); +} +#endif /* Py_WIN_WIDE_FILENAMES */ + + static PyObject * posix_error_with_allocated_filename(char* name) { @@ -390,6 +399,40 @@ win32_error(char* function, char* filename) else return PyErr_SetFromWindowsErr(errno); } + +#ifdef Py_WIN_WIDE_FILENAMES +static PyObject * +win32_error_unicode(char* function, Py_UNICODE* filename) +{ + /* XXX - see win32_error for comments on 'function' */ + errno = GetLastError(); + if (filename) + return PyErr_SetFromWindowsErrWithUnicodeFilename(errno, filename); + else + return PyErr_SetFromWindowsErr(errno); +} + +static PyObject *_PyUnicode_FromFileSystemEncodedObject(register PyObject *obj) +{ + /* XXX Perhaps we should make this API an alias of + PyObject_Unicode() instead ?! */ + if (PyUnicode_CheckExact(obj)) { + Py_INCREF(obj); + return obj; + } + if (PyUnicode_Check(obj)) { + /* For a Unicode subtype that's not a Unicode object, + return a true Unicode object with the same data. */ + return PyUnicode_FromUnicode(PyUnicode_AS_UNICODE(obj), + PyUnicode_GET_SIZE(obj)); + } + return PyUnicode_FromEncodedObject(obj, + Py_FileSystemDefaultEncoding, + "strict"); +} + +#endif /* Py_WIN_WIDE_FILENAMES */ + #endif #if defined(PYOS_OS2) @@ -487,11 +530,50 @@ posix_fildes(PyObject *fdobj, int (*func)(int)) return Py_None; } +#ifdef Py_WIN_WIDE_FILENAMES +static int +unicode_file_names(void) +{ + static int canusewide = -1; + if (canusewide == -1) { + /* As per doc for ::GetVersion(), this is the correct test for + the Windows NT family. */ + canusewide = (GetVersion() < 0x80000000) ? 1 : 0; + } + return canusewide; +} +#endif + static PyObject * -posix_1str(PyObject *args, char *format, int (*func)(const char*)) +posix_1str(PyObject *args, char *format, int (*func)(const char*), + char *wformat, int (*wfunc)(const Py_UNICODE*)) { char *path1 = NULL; int res; +#ifdef Py_WIN_WIDE_FILENAMES + if (unicode_file_names()) { + PyUnicodeObject *po; + if (PyArg_ParseTuple(args, wformat, &po)) { + Py_BEGIN_ALLOW_THREADS + /* PyUnicode_AS_UNICODE OK without thread + lock as it is a simple dereference. */ + res = (*wfunc)(PyUnicode_AS_UNICODE(po)); + Py_END_ALLOW_THREADS + if (res < 0) + return posix_error(); + Py_INCREF(Py_None); + return Py_None; + } + /* Drop the argument parsing error as narrow + strings are also valid. */ + PyErr_Clear(); + } +#else + /* Platforms that don't support Unicode filenames + shouldn't be passing these extra params */ + assert(wformat==NULL && wfunc == NULL); +#endif + if (!PyArg_ParseTuple(args, format, Py_FileSystemDefaultEncoding, &path1)) return NULL; @@ -506,11 +588,54 @@ posix_1str(PyObject *args, char *format, int (*func)(const char*)) } static PyObject * -posix_2str(PyObject *args, char *format, - int (*func)(const char *, const char *)) +posix_2str(PyObject *args, + char *format, + int (*func)(const char *, const char *), + char *wformat, + int (*wfunc)(const Py_UNICODE *, const Py_UNICODE *)) { char *path1 = NULL, *path2 = NULL; int res; +#ifdef Py_WIN_WIDE_FILENAMES + if (unicode_file_names()) { + PyObject *po1; + PyObject *po2; + if (PyArg_ParseTuple(args, wformat, &po1, &po2)) { + if (PyUnicode_Check(po1) || PyUnicode_Check(po2)) { + PyObject *wpath1; + PyObject *wpath2; + wpath1 = _PyUnicode_FromFileSystemEncodedObject(po1); + wpath2 = _PyUnicode_FromFileSystemEncodedObject(po2); + if (!wpath1 || !wpath2) { + Py_XDECREF(wpath1); + Py_XDECREF(wpath2); + return NULL; + } + Py_BEGIN_ALLOW_THREADS + /* PyUnicode_AS_UNICODE OK without thread + lock as it is a simple dereference. */ + res = (*wfunc)(PyUnicode_AS_UNICODE(wpath1), + PyUnicode_AS_UNICODE(wpath2)); + Py_END_ALLOW_THREADS + Py_XDECREF(wpath1); + Py_XDECREF(wpath2); + if (res != 0) + return posix_error(); + Py_INCREF(Py_None); + return Py_None; + } + /* Else flow through as neither is Unicode. */ + } + /* Drop the argument parsing error as narrow + strings are also valid. */ + PyErr_Clear(); + } +#else + /* Platforms that don't support Unicode filenames + shouldn't be passing these extra params */ + assert(wformat==NULL && wfunc == NULL); +#endif + if (!PyArg_ParseTuple(args, format, Py_FileSystemDefaultEncoding, &path1, Py_FileSystemDefaultEncoding, &path2)) @@ -692,8 +817,11 @@ _pystat_fromstructstat(STRUCT_STAT st) } static PyObject * -posix_do_stat(PyObject *self, PyObject *args, char *format, - int (*statfunc)(const char *, STRUCT_STAT *)) +posix_do_stat(PyObject *self, PyObject *args, + char *format, + int (*statfunc)(const char *, STRUCT_STAT *), + char *wformat, + int (*wstatfunc)(const Py_UNICODE *, STRUCT_STAT *)) { STRUCT_STAT st; char *path = NULL; /* pass this to stat; do not free() it */ @@ -705,6 +833,50 @@ posix_do_stat(PyObject *self, PyObject *args, char *format, char pathcopy[MAX_PATH]; #endif /* MS_WINDOWS */ + +#ifdef Py_WIN_WIDE_FILENAMES + /* If on wide-character-capable OS see if argument + is Unicode and if so use wide API. */ + if (unicode_file_names()) { + PyUnicodeObject *po; + if (PyArg_ParseTuple(args, wformat, &po)) { + Py_UNICODE wpath[MAX_PATH+1]; + pathlen = wcslen(PyUnicode_AS_UNICODE(po)); + /* the library call can blow up if the file name is too long! */ + if (pathlen > MAX_PATH) { + errno = ENAMETOOLONG; + return posix_error(); + } + wcscpy(wpath, PyUnicode_AS_UNICODE(po)); + /* Remove trailing slash or backslash, unless it's the current + drive root (/ or \) or a specific drive's root (like c:\ or c:/). + */ + if (pathlen > 0 && + (wpath[pathlen-1]== L'\\' || wpath[pathlen-1] == L'/')) { + /* It does end with a slash -- exempt the root drive cases. */ + /* XXX UNC root drives should also be exempted? */ + if (pathlen == 1 || (pathlen == 3 && wpath[1] == L':')) + /* leave it alone */; + else { + /* nuke the trailing backslash */ + wpath[pathlen-1] = L'\0'; + } + } + Py_BEGIN_ALLOW_THREADS + /* PyUnicode_AS_UNICODE result OK without + thread lock as it is a simple dereference. */ + res = wstatfunc(wpath, &st); + Py_END_ALLOW_THREADS + if (res != 0) + return posix_error_with_unicode_filename(wpath); + return _pystat_fromstructstat(st); + } + /* Drop the argument parsing error as narrow strings + are also valid. */ + PyErr_Clear(); + } +#endif + if (!PyArg_ParseTuple(args, format, Py_FileSystemDefaultEncoding, &path)) return NULL; @@ -839,10 +1011,12 @@ Change the current working directory to the specified path."); static PyObject * posix_chdir(PyObject *self, PyObject *args) { -#if defined(PYOS_OS2) && defined(PYCC_GCC) - return posix_1str(args, "et:chdir", _chdir2); +#ifdef MS_WINDOWS + return posix_1str(args, "et:chdir", chdir, "U:chdir", _wchdir); +#elif defined(PYOS_OS2) && defined(PYCC_GCC) + return posix_1str(args, "et:chdir", _chdir2, NULL, NULL); #else - return posix_1str(args, "et:chdir", chdir); + return posix_1str(args, "et:chdir", chdir, NULL, NULL); #endif } @@ -892,7 +1066,7 @@ Change root directory to path."); static PyObject * posix_chroot(PyObject *self, PyObject *args) { - return posix_1str(args, "et:chroot", chroot); + return posix_1str(args, "et:chroot", chroot, NULL, NULL); } #endif @@ -1004,6 +1178,43 @@ posix_getcwd(PyObject *self, PyObject *args) return posix_error(); return PyString_FromString(buf); } + +PyDoc_STRVAR(posix_getcwdu__doc__, +"getcwdu() -> path\n\n\ +Return a unicode string representing the current working directory."); + +static PyObject * +posix_getcwdu(PyObject *self, PyObject *args) +{ + char buf[1026]; + char *res; + if (!PyArg_ParseTuple(args, ":getcwd")) + return NULL; + +#ifdef Py_WIN_WIDE_FILENAMES + if (unicode_file_names()) { + wchar_t *wres; + wchar_t wbuf[1026]; + Py_BEGIN_ALLOW_THREADS + wres = _wgetcwd(wbuf, sizeof wbuf/ sizeof wbuf[0]); + Py_END_ALLOW_THREADS + if (wres == NULL) + return posix_error(); + return PyUnicode_FromWideChar(wbuf, wcslen(wbuf)); + } +#endif + + Py_BEGIN_ALLOW_THREADS +#if defined(PYOS_OS2) && defined(PYCC_GCC) + res = _getcwd2(buf, sizeof buf); +#else + res = getcwd(buf, sizeof buf); +#endif + Py_END_ALLOW_THREADS + if (res == NULL) + return posix_error(); + return PyUnicode_Decode(buf, strlen(buf), Py_FileSystemDefaultEncoding,"strict"); +} #endif @@ -1015,7 +1226,7 @@ Create a hard link to a file."); static PyObject * posix_link(PyObject *self, PyObject *args) { - return posix_2str(args, "etet:link", link); + return posix_2str(args, "etet:link", link, NULL, NULL); } #endif /* HAVE_LINK */ @@ -1044,6 +1255,66 @@ posix_listdir(PyObject *self, PyObject *args) char *bufptr = namebuf; int len = sizeof(namebuf)/sizeof(namebuf[0]); +#ifdef Py_WIN_WIDE_FILENAMES + /* If on wide-character-capable OS see if argument + is Unicode and if so use wide API. */ + if (unicode_file_names()) { + PyUnicodeObject *po; + if (PyArg_ParseTuple(args, "U:listdir", &po)) { + WIN32_FIND_DATAW wFileData; + Py_UNICODE wnamebuf[MAX_PATH*2+5]; + Py_UNICODE wch; + wcsncpy(wnamebuf, PyUnicode_AS_UNICODE(po), MAX_PATH); + wnamebuf[MAX_PATH] = L'\0'; + len = wcslen(wnamebuf); + wch = (len > 0) ? wnamebuf[len-1] : L'\0'; + if (wch != L'/' && wch != L'\\' && wch != L':') + wnamebuf[len++] = L'/'; + wcscpy(wnamebuf + len, L"*.*"); + if ((d = PyList_New(0)) == NULL) + return NULL; + hFindFile = FindFirstFileW(wnamebuf, &wFileData); + if (hFindFile == INVALID_HANDLE_VALUE) { + errno = GetLastError(); + if (errno == ERROR_FILE_NOT_FOUND) { + return d; + } + Py_DECREF(d); + return win32_error_unicode("FindFirstFileW", wnamebuf); + } + do { + if (wFileData.cFileName[0] == L'.' && + (wFileData.cFileName[1] == L'\0' || + wFileData.cFileName[1] == L'.' && + wFileData.cFileName[2] == L'\0')) + continue; + v = PyUnicode_FromUnicode(wFileData.cFileName, wcslen(wFileData.cFileName)); + if (v == NULL) { + Py_DECREF(d); + d = NULL; + break; + } + if (PyList_Append(d, v) != 0) { + Py_DECREF(v); + Py_DECREF(d); + d = NULL; + break; + } + Py_DECREF(v); + } while (FindNextFileW(hFindFile, &wFileData) == TRUE); + + if (FindClose(hFindFile) == FALSE) { + Py_DECREF(d); + return win32_error_unicode("FindClose", wnamebuf); + } + return d; + } + /* Drop the argument parsing error as narrow strings + are also valid. */ + PyErr_Clear(); + } +#endif + if (!PyArg_ParseTuple(args, "et#:listdir", Py_FileSystemDefaultEncoding, &bufptr, &len)) return NULL; @@ -1061,7 +1332,8 @@ posix_listdir(PyObject *self, PyObject *args) if (hFindFile == INVALID_HANDLE_VALUE) { errno = GetLastError(); if (errno == ERROR_FILE_NOT_FOUND) - return PyList_New(0); + return d; + Py_DECREF(d); return win32_error("FindFirstFile", namebuf); } do { @@ -1085,8 +1357,10 @@ posix_listdir(PyObject *self, PyObject *args) Py_DECREF(v); } while (FindNextFile(hFindFile, &FileData) == TRUE); - if (FindClose(hFindFile) == FALSE) + if (FindClose(hFindFile) == FALSE) { + Py_DECREF(d); return win32_error("FindClose", namebuf); + } return d; @@ -1213,6 +1487,23 @@ posix__getfullpathname(PyObject *self, PyObject *args) int insize = sizeof(inbuf)/sizeof(inbuf[0]); char outbuf[MAX_PATH*2]; char *temp; +#ifdef Py_WIN_WIDE_FILENAMES + if (unicode_file_names()) { + PyUnicodeObject *po; + if (PyArg_ParseTuple(args, "U|:_getfullpathname", &po)) { + Py_UNICODE woutbuf[MAX_PATH*2]; + Py_UNICODE *wtemp; + if (!GetFullPathNameW(PyUnicode_AS_UNICODE(po), + sizeof(woutbuf)/sizeof(woutbuf[0]), + woutbuf, &wtemp)) + return win32_error("GetFullPathName", ""); + return PyUnicode_FromUnicode(woutbuf, wcslen(woutbuf)); + } + /* Drop the argument parsing error as narrow strings + are also valid. */ + PyErr_Clear(); + } +#endif if (!PyArg_ParseTuple (args, "et#:_getfullpathname", Py_FileSystemDefaultEncoding, &inbufp, &insize)) @@ -1234,6 +1525,27 @@ posix_mkdir(PyObject *self, PyObject *args) int res; char *path = NULL; int mode = 0777; + +#ifdef Py_WIN_WIDE_FILENAMES + if (unicode_file_names()) { + PyUnicodeObject *po; + if (PyArg_ParseTuple(args, "U|i:mkdir", &po)) { + Py_BEGIN_ALLOW_THREADS + /* PyUnicode_AS_UNICODE OK without thread lock as + it is a simple dereference. */ + res = _wmkdir(PyUnicode_AS_UNICODE(po)); + Py_END_ALLOW_THREADS + if (res < 0) + return posix_error(); + Py_INCREF(Py_None); + return Py_None; + } + /* Drop the argument parsing error as narrow strings + are also valid. */ + PyErr_Clear(); + } +#endif + if (!PyArg_ParseTuple(args, "et|i:mkdir", Py_FileSystemDefaultEncoding, &path, &mode)) return NULL; @@ -1302,7 +1614,11 @@ Rename a file or directory."); static PyObject * posix_rename(PyObject *self, PyObject *args) { - return posix_2str(args, "etet:rename", rename); +#ifdef MS_WINDOWS + return posix_2str(args, "etet:rename", rename, "OO:rename", _wrename); +#else + return posix_2str(args, "etet:rename", rename, NULL, NULL); +#endif } @@ -1313,7 +1629,11 @@ Remove a directory."); static PyObject * posix_rmdir(PyObject *self, PyObject *args) { - return posix_1str(args, "et:rmdir", rmdir); +#ifdef MS_WINDOWS + return posix_1str(args, "et:rmdir", rmdir, "U:rmdir", _wrmdir); +#else + return posix_1str(args, "et:rmdir", rmdir, NULL, NULL); +#endif } @@ -1324,7 +1644,11 @@ Perform a stat system call on the given path."); static PyObject * posix_stat(PyObject *self, PyObject *args) { - return posix_do_stat(self, args, "et:stat", STAT); +#ifdef MS_WINDOWS + return posix_do_stat(self, args, "et:stat", STAT, "U:stat", _wstati64); +#else + return posix_do_stat(self, args, "et:stat", STAT, NULL, NULL); +#endif } @@ -1376,7 +1700,11 @@ Remove a file (same as unlink(path))."); static PyObject * posix_unlink(PyObject *self, PyObject *args) { - return posix_1str(args, "et:remove", unlink); +#ifdef MS_WINDOWS + return posix_1str(args, "et:remove", unlink, "U:remove", _wunlink); +#else + return posix_1str(args, "et:remove", unlink, NULL, NULL); +#endif } @@ -4150,9 +4478,13 @@ static PyObject * posix_lstat(PyObject *self, PyObject *args) { #ifdef HAVE_LSTAT - return posix_do_stat(self, args, "et:lstat", lstat); + return posix_do_stat(self, args, "et:lstat", lstat, NULL, NULL); #else /* !HAVE_LSTAT */ - return posix_do_stat(self, args, "et:lstat", STAT); +#ifdef MS_WINDOWS + return posix_do_stat(self, args, "et:lstat", STAT, "u:lstat", _wstati64); +#else + return posix_do_stat(self, args, "et:lstat", STAT, NULL, NULL); +#endif #endif /* !HAVE_LSTAT */ } @@ -4188,7 +4520,7 @@ Create a symbolic link."); static PyObject * posix_symlink(PyObject *self, PyObject *args) { - return posix_2str(args, "etet:symlink", symlink); + return posix_2str(args, "etet:symlink", symlink, NULL, NULL); } #endif /* HAVE_SYMLINK */ @@ -4369,6 +4701,26 @@ posix_open(PyObject *self, PyObject *args) int flag; int mode = 0777; int fd; + +#ifdef MS_WINDOWS + if (unicode_file_names()) { + PyUnicodeObject *po; + if (PyArg_ParseTuple(args, "Ui|i:mkdir", &po, &flag, &mode)) { + Py_BEGIN_ALLOW_THREADS + /* PyUnicode_AS_UNICODE OK without thread + lock as it is a simple dereference. */ + fd = _wopen(PyUnicode_AS_UNICODE(po), flag, mode); + Py_END_ALLOW_THREADS + if (fd < 0) + return posix_error(); + return PyInt_FromLong((long)fd); + } + /* Drop the argument parsing error as narrow strings + are also valid. */ + PyErr_Clear(); + } +#endif + if (!PyArg_ParseTuple(args, "eti|i", Py_FileSystemDefaultEncoding, &file, &flag, &mode)) @@ -6341,6 +6693,7 @@ static PyMethodDef posix_methods[] = { #endif #ifdef HAVE_GETCWD {"getcwd", posix_getcwd, METH_VARARGS, posix_getcwd__doc__}, + {"getcwdu", posix_getcwdu, METH_VARARGS, posix_getcwdu__doc__}, #endif #ifdef HAVE_LINK {"link", posix_link, METH_VARARGS, posix_link__doc__}, diff --git a/Objects/fileobject.c b/Objects/fileobject.c index 8dc21b7..ebf0d40 100644 --- a/Objects/fileobject.c +++ b/Objects/fileobject.c @@ -15,6 +15,12 @@ #include <windows.h> #endif +#ifdef _MSC_VER +/* Need GetVersion to see if on NT so safe to use _wfopen */ +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#endif /* _MSC_VER */ + #ifdef macintosh #ifdef USE_GUSI #define HAVE_FTRUNCATE @@ -102,7 +108,7 @@ dircheck(PyFileObject* f) static PyObject * fill_file_fields(PyFileObject *f, FILE *fp, char *name, char *mode, - int (*close)(FILE *)) + int (*close)(FILE *), PyObject *wname) { assert(f != NULL); assert(PyFile_Check(f)); @@ -110,7 +116,10 @@ fill_file_fields(PyFileObject *f, FILE *fp, char *name, char *mode, Py_DECREF(f->f_name); Py_DECREF(f->f_mode); - f->f_name = PyString_FromString(name); + if (wname) + f->f_name = PyUnicode_FromObject(wname); + else + f->f_name = PyString_FromString(name); f->f_mode = PyString_FromString(mode); f->f_close = close; @@ -135,7 +144,12 @@ open_the_file(PyFileObject *f, char *name, char *mode) { assert(f != NULL); assert(PyFile_Check(f)); +#ifdef MS_WINDOWS + /* windows ignores the passed name in order to support Unicode */ + assert(f->f_name != NULL); +#else assert(name != NULL); +#endif assert(mode != NULL); assert(f->f_fp == NULL); @@ -156,7 +170,6 @@ open_the_file(PyFileObject *f, char *name, char *mode) else #endif { - Py_BEGIN_ALLOW_THREADS #ifdef WITH_UNIVERSAL_NEWLINES if (strcmp(mode, "U") == 0 || strcmp(mode, "rU") == 0) mode = "rb"; @@ -168,8 +181,26 @@ open_the_file(PyFileObject *f, char *name, char *mode) if (strcmp(mode, "U") == 0 || strcmp(mode, "rU") == 0) mode = "r"; #endif - f->f_fp = fopen(name, mode); - Py_END_ALLOW_THREADS +#ifdef MS_WINDOWS + if (PyUnicode_Check(f->f_name)) { + PyObject *wmode; + wmode = PyUnicode_DecodeASCII(mode, strlen(mode), NULL); + if (f->f_name && wmode) { + Py_BEGIN_ALLOW_THREADS + /* PyUnicode_AS_UNICODE OK without thread + lock as it is a simple dereference. */ + f->f_fp = _wfopen(PyUnicode_AS_UNICODE(f->f_name), + PyUnicode_AS_UNICODE(wmode)); + Py_END_ALLOW_THREADS + } + Py_XDECREF(wmode); + } +#endif + if (NULL == f->f_fp && NULL != name) { + Py_BEGIN_ALLOW_THREADS + f->f_fp = fopen(name, mode); + Py_END_ALLOW_THREADS + } } if (f->f_fp == NULL) { #ifdef NO_FOPEN_ERRNO @@ -201,7 +232,11 @@ open_the_file(PyFileObject *f, char *name, char *mode) PyErr_Format(PyExc_IOError, "invalid mode: %s", mode); else +#ifdef MS_WINDOWS + PyErr_SetFromErrnoWithFilenameObject(PyExc_IOError, f->f_name); +#else PyErr_SetFromErrnoWithFilename(PyExc_IOError, name); +#endif /* MS_WINDOWS */ f = NULL; } if (f != NULL) @@ -215,7 +250,7 @@ PyFile_FromFile(FILE *fp, char *name, char *mode, int (*close)(FILE *)) PyFileObject *f = (PyFileObject *)PyFile_Type.tp_new(&PyFile_Type, NULL, NULL); if (f != NULL) { - if (fill_file_fields(f, fp, name, mode, close) == NULL) { + if (fill_file_fields(f, fp, name, mode, close, NULL) == NULL) { Py_DECREF(f); f = NULL; } @@ -293,11 +328,24 @@ file_dealloc(PyFileObject *f) static PyObject * file_repr(PyFileObject *f) { - return PyString_FromFormat("<%s file '%s', mode '%s' at %p>", + if (PyUnicode_Check(f->f_name)) { + PyObject *ret = NULL; + PyObject *name; + name = PyUnicode_AsUnicodeEscapeString(f->f_name); + ret = PyString_FromFormat("<%s file u'%s', mode '%s' at %p>", + f->f_fp == NULL ? "closed" : "open", + PyString_AsString(name), + PyString_AsString(f->f_mode), + f); + Py_XDECREF(name); + return ret; + } else { + return PyString_FromFormat("<%s file '%s', mode '%s' at %p>", f->f_fp == NULL ? "closed" : "open", PyString_AsString(f->f_name), PyString_AsString(f->f_mode), f); + } } static PyObject * @@ -1766,6 +1814,7 @@ file_init(PyObject *self, PyObject *args, PyObject *kwds) char *name = NULL; char *mode = "r"; int bufsize = -1; + int wideargument = 0; assert(PyFile_Check(self)); if (foself->f_fp != NULL) { @@ -1776,12 +1825,33 @@ file_init(PyObject *self, PyObject *args, PyObject *kwds) Py_DECREF(closeresult); } - if (!PyArg_ParseTupleAndKeywords(args, kwds, "et|si:file", kwlist, - Py_FileSystemDefaultEncoding, &name, - &mode, &bufsize)) - return -1; - if (fill_file_fields(foself, NULL, name, mode, fclose) == NULL) - goto Error; +#ifdef Py_WIN_WIDE_FILENAMES + if (GetVersion() < 0x80000000) { /* On NT, so wide API available */ + PyObject *po; + if (PyArg_ParseTupleAndKeywords(args, kwds, "U|si:file", + kwlist, &po, &mode, &bufsize)) { + wideargument = 1; + if (fill_file_fields(foself, NULL, name, mode, + fclose, po) == NULL) + goto Error; + } else { + /* Drop the argument parsing error as narrow + strings are also valid. */ + PyErr_Clear(); + } + } +#endif + + if (!wideargument) { + if (!PyArg_ParseTupleAndKeywords(args, kwds, "et|si:file", kwlist, + Py_FileSystemDefaultEncoding, + &name, + &mode, &bufsize)) + return -1; + if (fill_file_fields(foself, NULL, name, mode, + fclose, NULL) == NULL) + goto Error; + } if (open_the_file(foself, name, mode) == NULL) goto Error; PyFile_SetBufSize(self, bufsize); diff --git a/PC/pyconfig.h b/PC/pyconfig.h index a1e947c..a4fa88d 100644 --- a/PC/pyconfig.h +++ b/PC/pyconfig.h @@ -400,6 +400,10 @@ Py_NO_ENABLE_SHARED to find out. Also support MS_NO_COREDLL for b/w compat */ Include/unicodeobject.h). */ #if Py_UNICODE_SIZE == 2 #define HAVE_USABLE_WCHAR_T + +/* Define to indicate that the Python Unicode representation can be passed + as-is to Win32 Wide API. */ +#define Py_WIN_WIDE_FILENAMES #endif /* Use Python's own small-block memory-allocator. */ diff --git a/Python/errors.c b/Python/errors.c index 61d1df0..ccc2ae3 100644 --- a/Python/errors.c +++ b/Python/errors.c @@ -259,7 +259,7 @@ PyErr_NoMemory(void) } PyObject * -PyErr_SetFromErrnoWithFilename(PyObject *exc, char *filename) +PyErr_SetFromErrnoWithFilenameObject(PyObject *exc, PyObject *filenameObject) { PyObject *v; char *s; @@ -314,8 +314,8 @@ PyErr_SetFromErrnoWithFilename(PyObject *exc, char *filename) } #endif /* Unix/Windows */ #endif /* PLAN 9*/ - if (filename != NULL) - v = Py_BuildValue("(iss)", i, s, filename); + if (filenameObject != NULL) + v = Py_BuildValue("(isO)", i, s, filenameObject); else v = Py_BuildValue("(is)", i, s); if (v != NULL) { @@ -330,17 +330,39 @@ PyErr_SetFromErrnoWithFilename(PyObject *exc, char *filename) PyObject * +PyErr_SetFromErrnoWithFilename(PyObject *exc, char *filename) +{ + PyObject *name = filename ? PyString_FromString(filename) : NULL; + PyObject *result = PyErr_SetFromErrnoWithFilenameObject(exc, name); + Py_DECREF(name); + return result; +} + +#ifdef Py_WIN_WIDE_FILENAMES +PyObject * +PyErr_SetFromErrnoWithUnicodeFilename(PyObject *exc, Py_UNICODE *filename) +{ + PyObject *name = filename ? + PyUnicode_FromUnicode(filename, wcslen(filename)) : + NULL; + PyObject *result = PyErr_SetFromErrnoWithFilenameObject(exc, name); + Py_XDECREF(name); + return result; +} +#endif /* Py_WIN_WIDE_FILENAMES */ + +PyObject * PyErr_SetFromErrno(PyObject *exc) { - return PyErr_SetFromErrnoWithFilename(exc, NULL); + return PyErr_SetFromErrnoWithFilenameObject(exc, NULL); } #ifdef MS_WINDOWS /* Windows specific error code handling */ -PyObject *PyErr_SetExcFromWindowsErrWithFilename( +PyObject *PyErr_SetExcFromWindowsErrWithFilenameObject( PyObject *exc, int ierr, - const char *filename) + PyObject *filenameObject) { int len; char *s; @@ -362,8 +384,8 @@ PyObject *PyErr_SetExcFromWindowsErrWithFilename( /* remove trailing cr/lf and dots */ while (len > 0 && (s[len-1] <= ' ' || s[len-1] == '.')) s[--len] = '\0'; - if (filename != NULL) - v = Py_BuildValue("(iss)", err, s, filename); + if (filenameObject != NULL) + v = Py_BuildValue("(isO)", err, s, filenameObject); else v = Py_BuildValue("(is)", err, s); if (v != NULL) { @@ -374,6 +396,36 @@ PyObject *PyErr_SetExcFromWindowsErrWithFilename( return NULL; } +PyObject *PyErr_SetExcFromWindowsErrWithFilename( + PyObject *exc, + int ierr, + const char *filename) +{ + PyObject *name = filename ? PyString_FromString(filename) : NULL; + PyObject *ret = PyErr_SetExcFromWindowsErrWithFilenameObject(exc, + ierr, + name); + Py_XDECREF(name); + return ret; +} + +#ifdef Py_WIN_WIDE_FILENAMES +PyObject *PyErr_SetExcFromWindowsErrWithUnicodeFilename( + PyObject *exc, + int ierr, + const Py_UNICODE *filename) +{ + PyObject *name = filename ? + PyUnicode_FromUnicode(filename, wcslen(filename)) : + NULL; + PyObject *ret = PyErr_SetExcFromWindowsErrWithFilenameObject(exc, + ierr, + name); + Py_XDECREF(name); + return ret; +} +#endif /* Py_WIN_WIDE_FILENAMES */ + PyObject *PyErr_SetExcFromWindowsErr(PyObject *exc, int ierr) { return PyErr_SetExcFromWindowsErrWithFilename(exc, ierr, NULL); @@ -388,9 +440,29 @@ PyObject *PyErr_SetFromWindowsErrWithFilename( int ierr, const char *filename) { - return PyErr_SetExcFromWindowsErrWithFilename(PyExc_WindowsError, - ierr, filename); + PyObject *name = filename ? PyString_FromString(filename) : NULL; + PyObject *result = PyErr_SetExcFromWindowsErrWithFilenameObject( + PyExc_WindowsError, + ierr, name); + Py_XDECREF(result); + return result; +} + +#ifdef Py_WIN_WIDE_FILENAMES +PyObject *PyErr_SetFromWindowsErrWithUnicodeFilename( + int ierr, + const Py_UNICODE *filename) +{ + PyObject *name = filename ? + PyUnicode_FromUnicode(filename, wcslen(filename)) : + NULL; + PyObject *result = PyErr_SetExcFromWindowsErrWithFilenameObject( + PyExc_WindowsError, + ierr, name); + Py_XDECREF(result); + return result; } +#endif /* Py_WIN_WIDE_FILENAMES */ #endif /* MS_WINDOWS */ void |