--- Modules/posixmodule.c.orig 2008-08-22 15:10:49.000000000 +0200 +++ Modules/posixmodule.c 2008-08-22 16:00:42.000000000 +0200 @@ -331,6 +331,40 @@ #endif #endif +PyObject* getInvalideFilenameType(void) +{ + PyObject *module; + + /* FIXME: delete ref at exit */ + static PyObject* InvalidFilenameType = NULL; + + module = PyImport_ImportModule("filename"); + if (module == NULL) + return NULL; + InvalidFilenameType = PyObject_GetAttrString(module, "InvalidFilename"); + Py_DECREF(module); + return InvalidFilenameType; +} + +char* posix_get_filename(PyObject *args) +{ + PyObject *filenameobj, *pathobj, *type; + + if (!PyArg_ParseTuple(args, "O", &filenameobj)) + return NULL; + + type = getInvalideFilenameType(); + if (!type) + return NULL; + + if (!PyObject_IsInstance(filenameobj, type)) + return NULL; + pathobj = PyObject_GetAttrString(filenameobj, "bytes"); + if (!pathobj) + return NULL; + return PyBytes_AsString(pathobj); +} + /* Return a dictionary corresponding to the POSIX environment table */ #ifdef WITH_NEXT_FRAMEWORK /* On Darwin/MacOSX a shared library or framework has no access to @@ -637,16 +671,23 @@ posix_1str(PyObject *args, char *format, int (*func)(const char*)) { char *path1 = NULL; + int freepath = 1; int res; if (!PyArg_ParseTuple(args, format, Py_FileSystemDefaultEncoding, &path1)) - return NULL; + { + path1 = posix_get_filename(args); + if (!path1) + return NULL; + freepath = 0; + } Py_BEGIN_ALLOW_THREADS res = (*func)(path1); Py_END_ALLOW_THREADS if (res < 0) return posix_error_with_allocated_filename(path1); - PyMem_Free(path1); + if (freepath) + PyMem_Free(path1); Py_INCREF(Py_None); return Py_None; } @@ -1494,8 +1535,14 @@ if (!PyArg_ParseTuple(args, format, Py_FileSystemDefaultEncoding, &path)) - return NULL; - pathfree = path; + { + path = posix_get_filename(args); + if (!path) + return NULL; + pathfree = NULL; + } else { + pathfree = path; + } Py_BEGIN_ALLOW_THREADS res = (*statfunc)(path, &st); @@ -1511,7 +1558,8 @@ else result = _pystat_fromstructstat(&st); - PyMem_Free(pathfree); + if (pathfree) + PyMem_Free(pathfree); return result; } @@ -2087,7 +2135,7 @@ entries '.' and '..' even if they are present in the directory."); static PyObject * -posix_listdir(PyObject *self, PyObject *args) +posix_listdir(PyObject *self, PyObject *args, PyObject *kwargs) { /* XXX Should redo this putting the (now four) versions of opendir in separate files instead of having them all here... */ @@ -2327,9 +2375,12 @@ DIR *dirp; struct dirent *ep; int arg_is_unicode = 1; + int accept_invalid_filename = 0; + static char* kwnames[] = { "path", "invalid_filename", NULL }; errno = 0; - if (!PyArg_ParseTuple(args, "U:listdir", &v)) { + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "U|i:listdir", kwnames, + &v, &accept_invalid_filename)) { arg_is_unicode = 0; PyErr_Clear(); } @@ -2362,11 +2413,8 @@ (ep->d_name[1] == '.' && NAMLEN(ep) == 2))) continue; v = PyBytes_FromStringAndSize(ep->d_name, NAMLEN(ep)); - if (v == NULL) { - Py_DECREF(d); - d = NULL; - break; - } + if (v == NULL) + goto error; if (arg_is_unicode) { PyObject *w; @@ -2378,22 +2426,35 @@ v = w; } else { - /* fall back to the original byte string, as - discussed in patch #683592 */ - PyErr_Clear(); + PyObject *type; + if (!accept_invalid_filename) { + Py_DECREF(v); + goto error; + } + type = getInvalideFilenameType(); + + w = PyObject_CallFunction(type, "(Os)", v, Py_FileSystemDefaultEncoding); + if (!w) + return NULL; + Py_DECREF(v); + v = w; } } if (PyList_Append(d, v) != 0) { Py_DECREF(v); - Py_DECREF(d); - d = NULL; - break; + goto error; } Py_DECREF(v); } - closedir(dirp); PyMem_Free(name); + goto exit; +error: + closedir(dirp); + Py_DECREF(d); + d = NULL; + +exit: return d; #endif /* which OS */ @@ -2990,7 +3051,7 @@ return PyErr_NoMemory(); } for (i = 0; i < argc; i++) { - if (!PyArg_Parse((*getitem)(argv, i), format, + if (!PyArg_Parse((*getitem)(argv, i), "et", Py_FileSystemDefaultEncoding, &argvlist[i])) { free_string_array(argvlist, i); @@ -6816,7 +6877,7 @@ #ifdef HAVE_LINK {"link", posix_link, METH_VARARGS, posix_link__doc__}, #endif /* HAVE_LINK */ - {"listdir", posix_listdir, METH_VARARGS, posix_listdir__doc__}, + {"listdir", (PyCFunction)posix_listdir, METH_VARARGS|METH_KEYWORDS, posix_listdir__doc__}, {"lstat", posix_lstat, METH_VARARGS, posix_lstat__doc__}, {"mkdir", posix_mkdir, METH_VARARGS, posix_mkdir__doc__}, #ifdef HAVE_NICE Index: Lib/os.py =================================================================== --- Lib/os.py (révision 65973) +++ Lib/os.py (copie de travail) @@ -23,6 +23,7 @@ #' import sys, errno +from filename import InvalidFilename _names = sys.builtin_module_names @@ -657,3 +658,17 @@ raise TypeError("invalid fd type (%s, expected integer)" % type(fd)) import io return io.open(fd, *args, **kwargs) + +def fixed_join(a, *p): + """Join two or more pathname components, inserting '/' as needed. + If any component is an absolute path, all previous path components + will be discarded.""" + args = (a,) + p + for arg in args: + if not isinstance(arg, InvalidFilename): + continue + return InvalidFilename.join(args, arg.charset) + return fixed_join.default(a, *p) +fixed_join.default = path.join +path.join = fixed_join + Index: Lib/shutil.py =================================================================== --- Lib/shutil.py (révision 65973) +++ Lib/shutil.py (copie de travail) @@ -176,7 +176,7 @@ if errors: raise Error(errors) -def rmtree(path, ignore_errors=False, onerror=None): +def rmtree(path, ignore_errors=False, onerror=None, invalid_filename=True): """Recursively delete a directory tree. If ignore_errors is set, errors are ignored; otherwise, if onerror @@ -203,7 +203,7 @@ return names = [] try: - names = os.listdir(path) + names = os.listdir(path, invalid_filename=invalid_filename) except os.error as err: onerror(os.listdir, path, sys.exc_info()) for name in names: --- /dev/null 2008-03-02 21:54:29.000000000 +0100 +++ Lib/filename.py 2008-08-22 16:16:55.000000000 +0200 @@ -0,0 +1,28 @@ +class InvalidFilename: + def __init__(self, filename, charset): + self.bytes = filename + self.charset = charset + self.str = str(filename, charset, "replace") + + @staticmethod + def join(parts, charset): + path = b'' + for part in parts: + if isinstance(part, InvalidFilename): + part = part.bytes + else: + part = part.encode(charset) + if part.startswith(b'/'): + path = part + elif path == b'' or path.endswith(b'/'): + path += part + else: + path += b'/' + part + return InvalidFilename(path, charset) + + def __str__(self): + return self.str + + def __repr__(self): + return '' % (self.bytes, self.charset) +