Skip to content

Commit 2f02a51

Browse files
author
Victor Stinner
committed
PyUnicode_EncodeFS() raises an exception if _Py_wchar2char() fails
* Add error_pos optional argument to _Py_wchar2char() * PyUnicode_EncodeFS() raises a UnicodeEncodeError or MemoryError if _Py_wchar2char() fails
1 parent 0cfba09 commit 2f02a51

4 files changed

Lines changed: 37 additions & 11 deletions

File tree

Include/fileutils.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ PyAPI_FUNC(wchar_t *) _Py_char2wchar(
1010
size_t *size);
1111

1212
PyAPI_FUNC(char*) _Py_wchar2char(
13-
const wchar_t *text);
13+
const wchar_t *text,
14+
size_t *error_pos);
1415

1516
#if defined(HAVE_STAT) && !defined(MS_WINDOWS)
1617
PyAPI_FUNC(int) _Py_wstat(

Modules/main.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -646,7 +646,7 @@ Py_Main(int argc, wchar_t **argv)
646646
if (fp == NULL) {
647647
char *cfilename_buffer;
648648
const char *cfilename;
649-
cfilename_buffer = _Py_wchar2char(filename);
649+
cfilename_buffer = _Py_wchar2char(filename, NULL);
650650
if (cfilename_buffer != NULL)
651651
cfilename = cfilename_buffer;
652652
else

Objects/unicodeobject.c

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1606,14 +1606,31 @@ PyUnicode_EncodeFSDefault(PyObject *unicode)
16061606
wchar_t *wchar;
16071607
char *bytes;
16081608
PyObject *bytes_obj;
1609+
size_t error_pos;
16091610

16101611
wchar = PyUnicode_AsWideCharString(unicode, NULL);
16111612
if (wchar == NULL)
16121613
return NULL;
1613-
bytes = _Py_wchar2char(wchar);
1614-
PyMem_Free(wchar);
1615-
if (bytes == NULL)
1614+
bytes = _Py_wchar2char(wchar, &error_pos);
1615+
if (bytes == NULL) {
1616+
if (error_pos != (size_t)-1) {
1617+
char *errmsg = strerror(errno);
1618+
PyObject *exc = NULL;
1619+
if (errmsg == NULL)
1620+
errmsg = "Py_wchar2char() failed";
1621+
raise_encode_exception(&exc,
1622+
"filesystemencoding",
1623+
PyUnicode_AS_UNICODE(unicode), PyUnicode_GET_SIZE(unicode),
1624+
error_pos, error_pos+1,
1625+
errmsg);
1626+
Py_XDECREF(exc);
1627+
}
1628+
else
1629+
PyErr_NoMemory();
1630+
PyMem_Free(wchar);
16161631
return NULL;
1632+
}
1633+
PyMem_Free(wchar);
16171634

16181635
bytes_obj = PyBytes_FromString(bytes);
16191636
PyMem_Free(bytes);

Python/fileutils.c

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -132,15 +132,21 @@ _Py_char2wchar(const char* arg, size_t *size)
132132
This function is the reverse of _Py_char2wchar().
133133
134134
Return a pointer to a newly allocated byte string (use PyMem_Free() to free
135-
the memory), or NULL on error (conversion error or memory error). */
135+
the memory), or NULL on conversion or memory allocation error.
136+
137+
If error_pos is not NULL: *error_pos is the index of the invalid character
138+
on conversion error, or (size_t)-1 otherwise. */
136139
char*
137-
_Py_wchar2char(const wchar_t *text)
140+
_Py_wchar2char(const wchar_t *text, size_t *error_pos)
138141
{
139142
const size_t len = wcslen(text);
140143
char *result = NULL, *bytes = NULL;
141144
size_t i, size, converted;
142145
wchar_t c, buf[2];
143146

147+
if (error_pos != NULL)
148+
*error_pos = (size_t)-1;
149+
144150
/* The function works in two steps:
145151
1. compute the length of the output buffer in bytes (size)
146152
2. outputs the bytes */
@@ -168,6 +174,8 @@ _Py_wchar2char(const wchar_t *text)
168174
if (converted == (size_t)-1) {
169175
if (result != NULL)
170176
PyMem_Free(result);
177+
if (error_pos != NULL)
178+
*error_pos = i;
171179
return NULL;
172180
}
173181
if (bytes != NULL) {
@@ -208,7 +216,7 @@ _Py_wstat(const wchar_t* path, struct stat *buf)
208216
{
209217
int err;
210218
char *fname;
211-
fname = _Py_wchar2char(path);
219+
fname = _Py_wchar2char(path, NULL);
212220
if (fname == NULL) {
213221
errno = EINVAL;
214222
return -1;
@@ -263,7 +271,7 @@ _Py_wfopen(const wchar_t *path, const wchar_t *mode)
263271
errno = EINVAL;
264272
return NULL;
265273
}
266-
cpath = _Py_wchar2char(path);
274+
cpath = _Py_wchar2char(path, NULL);
267275
if (cpath == NULL)
268276
return NULL;
269277
f = fopen(cpath, cmode);
@@ -317,7 +325,7 @@ _Py_wreadlink(const wchar_t *path, wchar_t *buf, size_t bufsiz)
317325
int res;
318326
size_t r1;
319327

320-
cpath = _Py_wchar2char(path);
328+
cpath = _Py_wchar2char(path, NULL);
321329
if (cpath == NULL) {
322330
errno = EINVAL;
323331
return -1;
@@ -361,7 +369,7 @@ _Py_wrealpath(const wchar_t *path,
361369
wchar_t *wresolved_path;
362370
char *res;
363371
size_t r;
364-
cpath = _Py_wchar2char(path);
372+
cpath = _Py_wchar2char(path, NULL);
365373
if (cpath == NULL) {
366374
errno = EINVAL;
367375
return NULL;

0 commit comments

Comments
 (0)