diff --git a/Include/Python.h b/Include/Python.h index 6fbc49c..eb074fb 100644 --- a/Include/Python.h +++ b/Include/Python.h @@ -126,11 +126,14 @@ #ifdef __cplusplus extern "C" { #endif + /* _Py_Mangle is defined in compile.c */ PyAPI_FUNC(PyObject*) _Py_Mangle(PyObject *p, PyObject *name); -/* _Py_char2wchar lives in main.c */ +/* _Py_char2wchar and _Py_wchar2char live in main.c */ PyAPI_FUNC(wchar_t *) _Py_char2wchar(char *); +PyAPI_FUNC(char*) _Py_wchar2char(const wchar_t *text); + #ifdef __cplusplus } #endif diff --git a/Modules/getpath.c b/Modules/getpath.c index 4164a12..2334f0d 100644 --- a/Modules/getpath.c +++ b/Modules/getpath.c @@ -139,13 +139,16 @@ static wchar_t *lib_python = L"lib/python" VERSION; static int _wstat(const wchar_t* path, struct stat *buf) { - char fname[PATH_MAX]; - size_t res = wcstombs(fname, path, sizeof(fname)); - if (res == (size_t)-1) { + int err; + char *fname; + fname = _Py_wchar2char(path); + if (fname == NULL) { errno = EINVAL; return -1; } - return stat(fname, buf); + err = stat(fname, buf); + PyMem_Free(fname); + return err; } #endif diff --git a/Modules/main.c b/Modules/main.c index 7929b05..58094aa 100644 --- a/Modules/main.c +++ b/Modules/main.c @@ -105,20 +105,21 @@ PYTHONIOENCODING: Encoding[:errors] used for stdin/stdout/stderr.\n\ static FILE* _wfopen(const wchar_t *path, const wchar_t *mode) { - char cpath[PATH_MAX]; + FILE *f; + char *cpath; char cmode[10]; size_t r; - r = wcstombs(cpath, path, PATH_MAX); - if (r == (size_t)-1 || r >= PATH_MAX) { - errno = EINVAL; - return NULL; - } r = wcstombs(cmode, mode, 10); if (r == (size_t)-1 || r >= 10) { errno = EINVAL; return NULL; } - return fopen(cpath, cmode); + cpath = _Py_wchar2char(path); + if (cpath == NULL) + return NULL; + f = fopen(cpath, cmode); + PyMem_Free(cpath); + return f; } #endif @@ -731,6 +732,70 @@ Py_GetArgcArgv(int *argc, wchar_t ***argv) *argv = orig_argv; } +/* Encode a wide character string to the locale encoding with the + surrogateescape error handler (characters in range U+DC00..U+DCFF are + converted to bytes 0x00..0xff). + + Return a pointer to a newly allocated byte strings (use PyMem_Free() to free + the memory), or NULL on error (conversion error or memory error). */ +char* +_Py_wchar2char(const wchar_t *text) +{ + const size_t len = wcslen(text); + char *result = NULL, *bytes = NULL; + size_t i, size, converted; + wchar_t c, buf[2]; + + /* The function works in two steps: + 1. compute the length of the output buffer in bytes (size) + 2. write the output bytes */ + size = 0; + buf[1] = 0; + while (1) { + for (i=0; i < len; i++) { + c = text[i]; + if (c >= 0xdc00 && c <= 0xdcff) { + /* Surrogate character */ + if (bytes != NULL) { + *bytes++ = c - 0xdc00; + size--; + } + else + size++; + continue; + } + else { + buf[0] = c; + if (bytes != NULL) + converted = wcstombs(bytes, buf, size); + else + converted = wcstombs(NULL, buf, 0); + if (converted == (size_t)-1) { + if (result != NULL) + PyMem_Free(result); + return NULL; + } + if (bytes != NULL) { + bytes += converted; + size -= converted; + } + else + size += converted; + } + } + if (result != NULL) { + *bytes = 0; + break; + } + + size += 1; /* nul byte at the end */ + result = PyMem_Malloc(size); + if (result == NULL) + return NULL; + bytes = result; + } + return result; +} wchar_t* _Py_char2wchar(char* arg)