mirror of
https://github.com/python/cpython.git
synced 2025-11-03 19:34:08 +00:00
Add support for width, precision and zeropadding to the %d, %i, %u and %x
format specifiers in PyUnicode_FromFormat(). Change unicode's tp_str implementation to return a unicode object.
This commit is contained in:
parent
3e1b85ead1
commit
346737fc19
1 changed files with 86 additions and 30 deletions
|
|
@ -508,6 +508,28 @@ PyObject *PyUnicode_FromWideChar(register const wchar_t *w,
|
||||||
return (PyObject *)unicode;
|
return (PyObject *)unicode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
makefmt(char *fmt, int longflag, int size_tflag, int zeropad, int width, int precision, char c)
|
||||||
|
{
|
||||||
|
*fmt++ = '%';
|
||||||
|
if (width) {
|
||||||
|
if (zeropad)
|
||||||
|
*fmt++ = '0';
|
||||||
|
fmt += sprintf(fmt, "%d", width);
|
||||||
|
}
|
||||||
|
if (precision)
|
||||||
|
fmt += sprintf(fmt, ".%d", precision);
|
||||||
|
if (longflag)
|
||||||
|
*fmt++ = 'l';
|
||||||
|
else if (size_tflag) {
|
||||||
|
char *f = PY_FORMAT_SIZE_T;
|
||||||
|
while (*f)
|
||||||
|
*fmt++ = *f++;
|
||||||
|
}
|
||||||
|
*fmt++ = c;
|
||||||
|
*fmt = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
#define appendstring(string) {for (copy = string;*copy;) *s++ = *copy++;}
|
#define appendstring(string) {for (copy = string;*copy;) *s++ = *copy++;}
|
||||||
|
|
||||||
PyObject *
|
PyObject *
|
||||||
|
|
@ -518,11 +540,20 @@ PyUnicode_FromFormatV(const char *format, va_list vargs)
|
||||||
PyObject **callresults = NULL;
|
PyObject **callresults = NULL;
|
||||||
PyObject **callresult;
|
PyObject **callresult;
|
||||||
Py_ssize_t n = 0;
|
Py_ssize_t n = 0;
|
||||||
|
int width = 0;
|
||||||
|
int precision = 0;
|
||||||
|
int zeropad;
|
||||||
const char* f;
|
const char* f;
|
||||||
Py_UNICODE *s;
|
Py_UNICODE *s;
|
||||||
PyObject *string;
|
PyObject *string;
|
||||||
/* used by sprintf */
|
/* used by sprintf */
|
||||||
char buffer[21];
|
char buffer[21];
|
||||||
|
/* use abuffer instead of buffer, if we need more space
|
||||||
|
* (which can happen if there's a format specifier with width). */
|
||||||
|
char *abuffer = NULL;
|
||||||
|
char *realbuffer;
|
||||||
|
Py_ssize_t abuffersize = 0;
|
||||||
|
char fmt[60]; /* should be enough for %0width.precisionld */
|
||||||
const char *copy;
|
const char *copy;
|
||||||
|
|
||||||
#ifdef VA_LIST_IS_ARRAY
|
#ifdef VA_LIST_IS_ARRAY
|
||||||
|
|
@ -555,6 +586,9 @@ PyUnicode_FromFormatV(const char *format, va_list vargs)
|
||||||
for (f = format; *f; f++) {
|
for (f = format; *f; f++) {
|
||||||
if (*f == '%') {
|
if (*f == '%') {
|
||||||
const char* p = f;
|
const char* p = f;
|
||||||
|
width = 0;
|
||||||
|
while (isdigit(Py_CHARMASK(*f)))
|
||||||
|
width = (width*10) + *f++ - '0';
|
||||||
while (*++f && *f != '%' && !isalpha(Py_CHARMASK(*f)))
|
while (*++f && *f != '%' && !isalpha(Py_CHARMASK(*f)))
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|
@ -576,8 +610,14 @@ PyUnicode_FromFormatV(const char *format, va_list vargs)
|
||||||
(void) va_arg(count, int);
|
(void) va_arg(count, int);
|
||||||
/* 20 bytes is enough to hold a 64-bit
|
/* 20 bytes is enough to hold a 64-bit
|
||||||
integer. Decimal takes the most space.
|
integer. Decimal takes the most space.
|
||||||
This isn't enough for octal. */
|
This isn't enough for octal.
|
||||||
n += 20;
|
If a width is specified we need more
|
||||||
|
(which we allocate later). */
|
||||||
|
if (width < 20)
|
||||||
|
width = 20;
|
||||||
|
n += width;
|
||||||
|
if (abuffersize < width)
|
||||||
|
abuffersize = width;
|
||||||
break;
|
break;
|
||||||
case 's':
|
case 's':
|
||||||
n += strlen(va_arg(count, char*));
|
n += strlen(va_arg(count, char*));
|
||||||
|
|
@ -638,13 +678,23 @@ PyUnicode_FromFormatV(const char *format, va_list vargs)
|
||||||
n++;
|
n++;
|
||||||
}
|
}
|
||||||
expand:
|
expand:
|
||||||
|
if (abuffersize > 20) {
|
||||||
|
abuffer = PyMem_Malloc(abuffersize);
|
||||||
|
if (!abuffer) {
|
||||||
|
PyErr_NoMemory();
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
realbuffer = abuffer;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
realbuffer = buffer;
|
||||||
/* step 4: fill the buffer */
|
/* step 4: fill the buffer */
|
||||||
/* Since we've analyzed how much space we need for the worst case,
|
/* Since we've analyzed how much space we need for the worst case,
|
||||||
we don't have to resize the string.
|
we don't have to resize the string.
|
||||||
There can be no errors beyond this point. */
|
There can be no errors beyond this point. */
|
||||||
string = PyUnicode_FromUnicode(NULL, n);
|
string = PyUnicode_FromUnicode(NULL, n);
|
||||||
if (!string)
|
if (!string)
|
||||||
return NULL;
|
goto fail;
|
||||||
|
|
||||||
s = PyUnicode_AS_UNICODE(string);
|
s = PyUnicode_AS_UNICODE(string);
|
||||||
callresult = callresults;
|
callresult = callresults;
|
||||||
|
|
@ -654,19 +704,17 @@ PyUnicode_FromFormatV(const char *format, va_list vargs)
|
||||||
const char* p = f++;
|
const char* p = f++;
|
||||||
int longflag = 0;
|
int longflag = 0;
|
||||||
int size_tflag = 0;
|
int size_tflag = 0;
|
||||||
/* parse the width.precision part (we're only
|
zeropad = (*f == '0');
|
||||||
interested in the precision value, if any) */
|
/* parse the width.precision part */
|
||||||
n = 0;
|
width = 0;
|
||||||
while (isdigit(Py_CHARMASK(*f)))
|
while (isdigit(Py_CHARMASK(*f)))
|
||||||
n = (n*10) + *f++ - '0';
|
width = (width*10) + *f++ - '0';
|
||||||
|
precision = 0;
|
||||||
if (*f == '.') {
|
if (*f == '.') {
|
||||||
f++;
|
f++;
|
||||||
n = 0;
|
|
||||||
while (isdigit(Py_CHARMASK(*f)))
|
while (isdigit(Py_CHARMASK(*f)))
|
||||||
n = (n*10) + *f++ - '0';
|
precision = (precision*10) + *f++ - '0';
|
||||||
}
|
}
|
||||||
while (*f && *f != '%' && !isalpha(Py_CHARMASK(*f)))
|
|
||||||
f++;
|
|
||||||
/* handle the long flag, but only for %ld and %lu.
|
/* handle the long flag, but only for %ld and %lu.
|
||||||
others can be added when necessary. */
|
others can be added when necessary. */
|
||||||
if (*f == 'l' && (f[1] == 'd' || f[1] == 'u')) {
|
if (*f == 'l' && (f[1] == 'd' || f[1] == 'u')) {
|
||||||
|
|
@ -684,34 +732,34 @@ PyUnicode_FromFormatV(const char *format, va_list vargs)
|
||||||
*s++ = va_arg(vargs, int);
|
*s++ = va_arg(vargs, int);
|
||||||
break;
|
break;
|
||||||
case 'd':
|
case 'd':
|
||||||
|
makefmt(fmt, longflag, size_tflag, zeropad, width, precision, 'd');
|
||||||
if (longflag)
|
if (longflag)
|
||||||
sprintf(buffer, "%ld", va_arg(vargs, long));
|
sprintf(realbuffer, fmt, va_arg(vargs, long));
|
||||||
else if (size_tflag)
|
else if (size_tflag)
|
||||||
sprintf(buffer, "%" PY_FORMAT_SIZE_T "d",
|
sprintf(realbuffer, fmt, va_arg(vargs, Py_ssize_t));
|
||||||
va_arg(vargs, Py_ssize_t));
|
|
||||||
else
|
else
|
||||||
sprintf(buffer, "%d", va_arg(vargs, int));
|
sprintf(realbuffer, fmt, va_arg(vargs, int));
|
||||||
appendstring(buffer);
|
appendstring(realbuffer);
|
||||||
break;
|
break;
|
||||||
case 'u':
|
case 'u':
|
||||||
|
makefmt(fmt, longflag, size_tflag, zeropad, width, precision, 'u');
|
||||||
if (longflag)
|
if (longflag)
|
||||||
sprintf(buffer, "%lu",
|
sprintf(realbuffer, fmt, va_arg(vargs, unsigned long));
|
||||||
va_arg(vargs, unsigned long));
|
|
||||||
else if (size_tflag)
|
else if (size_tflag)
|
||||||
sprintf(buffer, "%" PY_FORMAT_SIZE_T "u",
|
sprintf(realbuffer, fmt, va_arg(vargs, size_t));
|
||||||
va_arg(vargs, size_t));
|
|
||||||
else
|
else
|
||||||
sprintf(buffer, "%u",
|
sprintf(realbuffer, fmt, va_arg(vargs, unsigned int));
|
||||||
va_arg(vargs, unsigned int));
|
appendstring(realbuffer);
|
||||||
appendstring(buffer);
|
|
||||||
break;
|
break;
|
||||||
case 'i':
|
case 'i':
|
||||||
sprintf(buffer, "%i", va_arg(vargs, int));
|
makefmt(fmt, 0, 0, zeropad, width, precision, 'i');
|
||||||
appendstring(buffer);
|
sprintf(realbuffer, fmt, va_arg(vargs, int));
|
||||||
|
appendstring(realbuffer);
|
||||||
break;
|
break;
|
||||||
case 'x':
|
case 'x':
|
||||||
sprintf(buffer, "%x", va_arg(vargs, int));
|
makefmt(fmt, 0, 0, zeropad, width, precision, 'x');
|
||||||
appendstring(buffer);
|
sprintf(realbuffer, fmt, va_arg(vargs, int));
|
||||||
|
appendstring(realbuffer);
|
||||||
break;
|
break;
|
||||||
case 's':
|
case 's':
|
||||||
p = va_arg(vargs, char*);
|
p = va_arg(vargs, char*);
|
||||||
|
|
@ -767,6 +815,8 @@ PyUnicode_FromFormatV(const char *format, va_list vargs)
|
||||||
end:
|
end:
|
||||||
if (callresults)
|
if (callresults)
|
||||||
PyMem_Free(callresults);
|
PyMem_Free(callresults);
|
||||||
|
if (abuffer)
|
||||||
|
PyMem_Free(abuffer);
|
||||||
_PyUnicode_Resize(&string, s - PyUnicode_AS_UNICODE(string));
|
_PyUnicode_Resize(&string, s - PyUnicode_AS_UNICODE(string));
|
||||||
return string;
|
return string;
|
||||||
fail:
|
fail:
|
||||||
|
|
@ -778,6 +828,8 @@ PyUnicode_FromFormatV(const char *format, va_list vargs)
|
||||||
}
|
}
|
||||||
PyMem_Free(callresults);
|
PyMem_Free(callresults);
|
||||||
}
|
}
|
||||||
|
if (abuffer)
|
||||||
|
PyMem_Free(abuffer);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -7327,9 +7379,13 @@ unicode_splitlines(PyUnicodeObject *self, PyObject *args)
|
||||||
static
|
static
|
||||||
PyObject *unicode_str(PyObject *self)
|
PyObject *unicode_str(PyObject *self)
|
||||||
{
|
{
|
||||||
PyObject *res = _PyUnicode_AsDefaultEncodedString(self, NULL);
|
if (PyUnicode_CheckExact(self)) {
|
||||||
Py_XINCREF(res);
|
Py_INCREF(self);
|
||||||
return res;
|
return self;
|
||||||
|
} else
|
||||||
|
/* Subtype -- return genuine unicode string with the same value. */
|
||||||
|
return PyUnicode_FromUnicode(PyUnicode_AS_UNICODE(self),
|
||||||
|
PyUnicode_GET_SIZE(self));
|
||||||
}
|
}
|
||||||
|
|
||||||
PyDoc_STRVAR(swapcase__doc__,
|
PyDoc_STRVAR(swapcase__doc__,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue