Commit 5e9c80d9 authored by Andrew MacIntyre's avatar Andrew MacIntyre

%#x/%#X format conversion cleanup (see patch #450267):

  Objects/
    stringobject.c
    unicodeobject.c
parent bbea4717
...@@ -3069,46 +3069,52 @@ formatint(char *buf, size_t buflen, int flags, ...@@ -3069,46 +3069,52 @@ formatint(char *buf, size_t buflen, int flags,
+ 1 + 1 = 24 */ + 1 + 1 = 24 */
char fmt[64]; /* plenty big enough! */ char fmt[64]; /* plenty big enough! */
long x; long x;
if (!PyArg_Parse(v, "l;int argument required", &x)) if (!PyArg_Parse(v, "l;int argument required", &x))
return -1; return -1;
if (prec < 0) if (prec < 0)
prec = 1; prec = 1;
if ((flags & F_ALT) &&
(type == 'x' || type == 'X')) {
/* When converting under %#x or %#X, there are a number
* of issues that cause pain:
* - when 0 is being converted, the C standard leaves off
* the '0x' or '0X', which is inconsistent with other
* %#x/%#X conversions and inconsistent with Python's
* hex() function
* - there are platforms that violate the standard and
* convert 0 with the '0x' or '0X'
* (Metrowerks, Compaq Tru64)
* - there are platforms that give '0x' when converting
* under %#X, but convert 0 in accordance with the
* standard (OS/2 EMX)
*
* We can achieve the desired consistency by inserting our
* own '0x' or '0X' prefix, and substituting %x/%X in place
* of %#x/%#X.
*
* Note that this is the same approach as used in
* formatint() in unicodeobject.c
*/
PyOS_snprintf(fmt, sizeof(fmt), "0%c%%.%dl%c",
type, prec, type);
}
else {
PyOS_snprintf(fmt, sizeof(fmt), "%%%s.%dl%c", PyOS_snprintf(fmt, sizeof(fmt), "%%%s.%dl%c",
(flags&F_ALT) ? "#" : "", (flags&F_ALT) ? "#" : "",
prec, type); prec, type);
}
/* buf = '+'/'-'/'0'/'0x' + '[0-9]'*max(prec, len(x in octal)) /* buf = '+'/'-'/'0'/'0x' + '[0-9]'*max(prec, len(x in octal))
worst case buf = '0x' + [0-9]*prec, where prec >= 11 */ * worst case buf = '0x' + [0-9]*prec, where prec >= 11
*/
if (buflen <= 13 || buflen <= (size_t)2 + (size_t)prec) { if (buflen <= 13 || buflen <= (size_t)2 + (size_t)prec) {
PyErr_SetString(PyExc_OverflowError, PyErr_SetString(PyExc_OverflowError,
"formatted integer is too long (precision too large?)"); "formatted integer is too long (precision too large?)");
return -1; return -1;
} }
PyOS_snprintf(buf, buflen, fmt, x); PyOS_snprintf(buf, buflen, fmt, x);
/* When converting 0 under %#x or %#X, C leaves off the base marker,
* but we want it (for consistency with other %#x conversions, and
* for consistency with Python's hex() function).
* BUG 28-Apr-2001 tim: At least two platform Cs (Metrowerks &
* Compaq Tru64) violate the std by converting 0 w/ leading 0x anyway.
* So add it only if the platform didn't already.
*/
if (x == 0 &&
(flags & F_ALT) &&
(type == 'x' || type == 'X') &&
buf[1] != (char)type) /* this last always true under std C */
{
memmove(buf+2, buf, strlen(buf) + 1);
buf[0] = '0';
buf[1] = (char)type;
}
#if defined(PYOS_OS2) && defined(PYCC_GCC)
/* unfortunately, the EMX C runtime gives us '0x' as the base
* marker for %X when we expect/want '0X'
*/
else if ((flags & F_ALT) && (type == 'X')) {
assert(buf[1] == 'x');
buf[1] = (char)type;
}
#endif
return strlen(buf); return strlen(buf);
} }
......
...@@ -5137,53 +5137,58 @@ formatint(Py_UNICODE *buf, ...@@ -5137,53 +5137,58 @@ formatint(Py_UNICODE *buf,
PyObject *v) PyObject *v)
{ {
/* fmt = '%#.' + `prec` + 'l' + `type` /* fmt = '%#.' + `prec` + 'l' + `type`
worst case length = 3 + 19 (worst len of INT_MAX on 64-bit machine) * worst case length = 3 + 19 (worst len of INT_MAX on 64-bit machine)
+ 1 + 1 = 24*/ * + 1 + 1
* = 24
*/
char fmt[64]; /* plenty big enough! */ char fmt[64]; /* plenty big enough! */
long x; long x;
int use_native_c_format = 1;
x = PyInt_AsLong(v); x = PyInt_AsLong(v);
if (x == -1 && PyErr_Occurred()) if (x == -1 && PyErr_Occurred())
return -1; return -1;
if (prec < 0) if (prec < 0)
prec = 1; prec = 1;
/* buf = '+'/'-'/'0'/'0x' + '[0-9]'*max(prec,len(x in octal)) /* buf = '+'/'-'/'0'/'0x' + '[0-9]'*max(prec,len(x in octal))
worst case buf = '0x' + [0-9]*prec, where prec >= 11 */ * worst case buf = '0x' + [0-9]*prec, where prec >= 11
if (buflen <= 13 || buflen <= (size_t)2+(size_t)prec) { */
if (buflen <= 13 || buflen <= (size_t)2 + (size_t)prec) {
PyErr_SetString(PyExc_OverflowError, PyErr_SetString(PyExc_OverflowError,
"formatted integer is too long (precision too long?)"); "formatted integer is too long (precision too large?)");
return -1; return -1;
} }
/* When converting 0 under %#x or %#X, C leaves off the base marker,
* but we want it (for consistency with other %#x conversions, and if ((flags & F_ALT) &&
* for consistency with Python's hex() function). (type == 'x' || type == 'X')) {
* BUG 28-Apr-2001 tim: At least two platform Cs (Metrowerks & /* When converting under %#x or %#X, there are a number
* Compaq Tru64) violate the std by converting 0 w/ leading 0x anyway. * of issues that cause pain:
* So add it only if the platform doesn't already. * - when 0 is being converted, the C standard leaves off
*/ * the '0x' or '0X', which is inconsistent with other
#if defined(PYOS_OS2) && defined(PYCC_GCC) * %#x/%#X conversions and inconsistent with Python's
if ((flags & F_ALT) && (type == 'x' || type == 'X')) { * hex() function
/* the EMX runtime gives 0x as the base marker when we want 0X * - there are platforms that violate the standard and
* so we cover all bets by supplying our own for both cases. * convert 0 with the '0x' or '0X'
* (Metrowerks, Compaq Tru64)
* - there are platforms that give '0x' when converting
* under %#X, but convert 0 in accordance with the
* standard (OS/2 EMX)
*
* We can achieve the desired consistency by inserting our
* own '0x' or '0X' prefix, and substituting %x/%X in place
* of %#x/%#X.
*
* Note that this is the same approach as used in
* formatint() in stringobject.c
*/ */
use_native_c_format = 0; PyOS_snprintf(fmt, sizeof(fmt), "0%c%%.%dl%c",
PyOS_snprintf(fmt, sizeof(fmt), "0%c%%.%dl%c", type, prec, type); type, prec, type);
}
#else
if (x == 0 && (flags & F_ALT) && (type == 'x' || type == 'X')) {
/* Only way to know what the platform does is to try it. */
PyOS_snprintf(fmt, sizeof(fmt), type == 'x' ? "%#x" : "%#X", 0);
if (fmt[1] != (char)type) {
/* Supply our own leading 0x/0X -- needed under std C */
use_native_c_format = 0;
PyOS_snprintf(fmt, sizeof(fmt), "0%c%%#.%dl%c", type, prec, type);
}
} }
#endif else {
if (use_native_c_format)
PyOS_snprintf(fmt, sizeof(fmt), "%%%s.%dl%c", PyOS_snprintf(fmt, sizeof(fmt), "%%%s.%dl%c",
(flags & F_ALT) ? "#" : "", prec, type); (flags&F_ALT) ? "#" : "",
prec, type);
}
return usprintf(buf, fmt, x); return usprintf(buf, fmt, x);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment