]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
Patch 594001: PEP 277 - Unicode file name support for Windows NT.
authorMark Hammond <mhammond@skippinet.com.au>
Thu, 3 Oct 2002 05:10:39 +0000 (05:10 +0000)
committerMark Hammond <mhammond@skippinet.com.au>
Thu, 3 Oct 2002 05:10:39 +0000 (05:10 +0000)
Include/pyerrors.h
Lib/test/test_unicode_file.py
Modules/posixmodule.c
Objects/fileobject.c
PC/pyconfig.h
Python/errors.c

index 119aa2efb8cef46c3b1447daa93af77ad0fa82ca..18dee2876e6eb63c11a604e800d5dd12fa37f178 100644 (file)
@@ -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);
index a0de1394ed1dcd037a13df8fed4157538b9efacc..4bafd982f11e1fa8700b24c1a3b3710622cd7bb4 100644 (file)
@@ -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)
 
index 9ac0eb4993e24df4cdb01e81f4a0947e6cd5773f..494ca61bae64137fb64eaf87e6ab3a5c2fb10194 100644 (file)
@@ -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__},
index 8dc21b730f68ed3a8901fa7e2b60411154c3429f..ebf0d40d4200221d52b1ef852e6f1aa64568f471 100644 (file)
 #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);
index a1e947cff30729088e64073ed28401d940590420..a4fa88dac2265e363a5e7098357dcb130b483aec 100644 (file)
@@ -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. */
index 61d1df0d75aafd515044993bc0a17ccfccb63590..ccc2ae37bdcf88510be2d59974bddd484f6db93c 100644 (file)
@@ -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) {
@@ -329,18 +329,40 @@ 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