Avoiding round-tripping via mbcs on windows in strftime()

This commit is contained in:
Kovid Goyal 2018-02-14 03:14:50 +05:30
parent 1335ca8b54
commit ae4cd7c902
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
2 changed files with 52 additions and 70 deletions

View File

@ -543,9 +543,9 @@ def strftime(fmt, t=None):
t[0] = replacement
ans = None
if iswindows:
if isinstance(fmt, unicode):
fmt = fmt.encode('mbcs', 'replace')
fmt = fmt.replace(b'%e', b'%#d')
if isinstance(fmt, bytes):
fmt = fmt.decode('mbcs', 'replace')
fmt = fmt.replace(u'%e', u'%#d')
ans = plugins['winutil'][0].strftime(fmt, t)
else:
ans = time.strftime(fmt, t).decode(preferred_encoding, 'replace')

View File

@ -300,84 +300,69 @@ winutil_strftime(PyObject *self, PyObject *args)
{
PyObject *tup = NULL;
struct tm buf;
const char *_fmt;
size_t fmtlen, buflen;
wchar_t *outbuf = NULL, *fmt = NULL;
size_t buflen;
wchar_t *outbuf = NULL;
Py_UNICODE *fmt = NULL;
int fmtlen;
size_t i;
memset((void *) &buf, '\0', sizeof(buf));
memset((void *) &buf, 0, sizeof(buf));
if (!PyArg_ParseTuple(args, "s|O:strftime", &_fmt, &tup))
return NULL;
if (mbstowcs_s(&fmtlen, NULL, 0, _fmt, strlen(_fmt)) != 0) {
PyErr_SetString(PyExc_ValueError, "Failed to convert fmt to wchar");
return NULL;
}
fmt = (wchar_t *)PyMem_Malloc((fmtlen+2)*sizeof(wchar_t));
if (fmt == NULL) return PyErr_NoMemory();
if (mbstowcs_s(&fmtlen, fmt, fmtlen+2, _fmt, strlen(_fmt)) != 0) {
PyErr_SetString(PyExc_ValueError, "Failed to convert fmt to wchar");
goto end;
}
if (!PyArg_ParseTuple(args, "u#|O:strftime", &fmt, &fmtlen, &tup)) return NULL;
if (tup == NULL) {
time_t tt = time(NULL);
if(localtime_s(&buf, &tt) != 0) {
PyErr_SetString(PyExc_ValueError, "Failed to get localtime()");
goto end;
return NULL;
}
} else if (!gettmarg(tup, &buf))
goto end;
} else if (!gettmarg(tup, &buf)) return NULL;
if (buf.tm_mon == -1)
buf.tm_mon = 0;
if (buf.tm_mon == -1) buf.tm_mon = 0;
else if (buf.tm_mon < 0 || buf.tm_mon > 11) {
PyErr_SetString(PyExc_ValueError, "month out of range");
goto end;
}
if (buf.tm_mday == 0)
buf.tm_mday = 1;
PyErr_SetString(PyExc_ValueError, "month out of range");
return NULL;
}
if (buf.tm_mday == 0) buf.tm_mday = 1;
else if (buf.tm_mday < 0 || buf.tm_mday > 31) {
PyErr_SetString(PyExc_ValueError, "day of month out of range");
goto end;
}
if (buf.tm_hour < 0 || buf.tm_hour > 23) {
PyErr_SetString(PyExc_ValueError, "hour out of range");
goto end;
}
if (buf.tm_min < 0 || buf.tm_min > 59) {
PyErr_SetString(PyExc_ValueError, "minute out of range");
goto end;
}
if (buf.tm_sec < 0 || buf.tm_sec > 61) {
PyErr_SetString(PyExc_ValueError, "seconds out of range");
goto end;
}
/* tm_wday does not need checking of its upper-bound since taking
``% 7`` in gettmarg() automatically restricts the range. */
if (buf.tm_wday < 0) {
PyErr_SetString(PyExc_ValueError, "day of week out of range");
goto end;
}
if (buf.tm_yday == -1)
buf.tm_yday = 0;
PyErr_SetString(PyExc_ValueError, "day of month out of range");
return NULL;
}
if (buf.tm_hour < 0 || buf.tm_hour > 23) {
PyErr_SetString(PyExc_ValueError, "hour out of range");
return NULL;
}
if (buf.tm_min < 0 || buf.tm_min > 59) {
PyErr_SetString(PyExc_ValueError, "minute out of range");
return NULL;
}
if (buf.tm_sec < 0 || buf.tm_sec > 61) {
PyErr_SetString(PyExc_ValueError, "seconds out of range");
return NULL;
}
/* tm_wday does not need checking of its upper-bound since taking
``% 7`` in gettmarg() automatically restricts the range. */
if (buf.tm_wday < 0) {
PyErr_SetString(PyExc_ValueError, "day of week out of range");
return NULL;
}
if (buf.tm_yday == -1) buf.tm_yday = 0;
else if (buf.tm_yday < 0 || buf.tm_yday > 365) {
PyErr_SetString(PyExc_ValueError, "day of year out of range");
goto end;
}
if (buf.tm_isdst < -1 || buf.tm_isdst > 1) {
PyErr_SetString(PyExc_ValueError,
"daylight savings flag out of range");
goto end;
}
PyErr_SetString(PyExc_ValueError, "day of year out of range");
return NULL;
}
if (buf.tm_isdst < -1 || buf.tm_isdst > 1) {
PyErr_SetString(PyExc_ValueError,
"daylight savings flag out of range");
return NULL;
}
for (i = 5*fmtlen; ; i += i) {
for (i = 5*(unsigned int)fmtlen; ; i += i) {
outbuf = (wchar_t *)PyMem_Malloc(i*sizeof(wchar_t));
if (outbuf == NULL) {
PyErr_NoMemory(); goto end;
PyErr_NoMemory(); return NULL;
}
buflen = wcsftime(outbuf, i, fmt, &buf);
if (buflen > 0 || i >= 256 * fmtlen) {
if (buflen > 0 || i >= 256 * (unsigned int)fmtlen) {
/* If the buffer is 256 times as long as the format,
it's probably not failing for lack of room!
More likely, the format yields an empty result,
@ -385,20 +370,17 @@ winutil_strftime(PyObject *self, PyObject *args)
is unknown. */
PyObject *ret;
ret = PyUnicode_FromWideChar(outbuf, buflen);
PyMem_Free(outbuf); PyMem_Free(fmt);
PyMem_Free(outbuf);
return ret;
}
PyMem_Free(outbuf);
#if defined _MSC_VER && _MSC_VER >= 1400 && defined(__STDC_SECURE_LIB__)
/* VisualStudio .NET 2005 does this properly */
if (buflen == 0 && errno == EINVAL) {
PyErr_SetString(PyExc_ValueError, "Invalid format string");
goto end;
return NULL;
}
#endif
}
end:
PyMem_Free(fmt); return NULL;
return NULL;
}