PyString_FromFormat() and PyString_FromFormatV(): Largely ripped from

PyErr_Format() these new C API methods can be used instead of
    sprintf()'s into hardcoded char* buffers.  This allows us to fix
    many situation where long package, module, or class names get
    truncated in reprs.

    PyString_FromFormat() is the varargs variety.
    PyString_FromFormatV() is the va_list variety

    Original PyErr_Format() code was modified to allow %p and %ld
    expansions.

    Many reprs were converted to this, checkins coming soo.  Not
    changed: complex_repr(), float_repr(), float_print(), float_str(),
    int_repr().  There may be other candidates not yet converted.

    Closes patch #454743.
This commit is contained in:
Barry Warsaw 2001-08-24 18:32:06 +00:00
parent 16c018d2d2
commit dadace004b
2 changed files with 159 additions and 0 deletions

View file

@ -147,6 +147,161 @@ PyString_FromString(const char *str)
return (PyObject *) op;
}
PyObject *
PyString_FromFormatV(const char *format, va_list vargs)
{
va_list count = vargs;
int n = 0;
const char* f;
char *s;
PyObject* string;
/* step 1: figure out how large a buffer we need */
for (f = format; *f; f++) {
if (*f == '%') {
const char* p = f;
while (*++f && *f != '%' && !isalpha(Py_CHARMASK(*f)))
;
/* skip the 'l' in %ld, since it doesn't change the
width. although only %d is supported (see
"expand" section below), others can be easily
add */
if (*f == 'l' && *(f+1) == 'd')
++f;
switch (*f) {
case 'c':
(void)va_arg(count, int);
/* fall through... */
case '%':
n++;
break;
case 'd': case 'i': case 'x':
(void) va_arg(count, int);
/* 20 bytes should be enough to hold a 64-bit
integer */
n += 20;
break;
case 's':
s = va_arg(count, char*);
n += strlen(s);
break;
case 'p':
(void) va_arg(count, int);
/* maximum 64-bit pointer representation:
* 0xffffffffffffffff
* so 19 characters is enough.
*/
n += 19;
break;
default:
/* if we stumble upon an unknown
formatting code, copy the rest of
the format string to the output
string. (we cannot just skip the
code, since there's no way to know
what's in the argument list) */
n += strlen(p);
goto expand;
}
} else
n++;
}
expand:
/* step 2: fill the buffer */
string = PyString_FromStringAndSize(NULL, n);
if (!string)
return NULL;
s = PyString_AsString(string);
for (f = format; *f; f++) {
if (*f == '%') {
const char* p = f++;
int i, longflag = 0;
/* parse the width.precision part (we're only
interested in the precision value, if any) */
n = 0;
while (isdigit(Py_CHARMASK(*f)))
n = (n*10) + *f++ - '0';
if (*f == '.') {
f++;
n = 0;
while (isdigit(Py_CHARMASK(*f)))
n = (n*10) + *f++ - '0';
}
while (*f && *f != '%' && !isalpha(Py_CHARMASK(*f)))
f++;
/* handle the long flag, but only for %ld. others
can be added when necessary. */
if (*f == 'l' && *(f+1) == 'd') {
longflag = 1;
++f;
}
switch (*f) {
case 'c':
*s++ = va_arg(vargs, int);
break;
case 'd':
if (longflag)
sprintf(s, "%ld", va_arg(vargs, long));
else
sprintf(s, "%d", va_arg(vargs, int));
s += strlen(s);
break;
case 'i':
sprintf(s, "%i", va_arg(vargs, int));
s += strlen(s);
break;
case 'x':
sprintf(s, "%x", va_arg(vargs, int));
s += strlen(s);
break;
case 's':
p = va_arg(vargs, char*);
i = strlen(p);
if (n > 0 && i > n)
i = n;
memcpy(s, p, i);
s += i;
break;
case 'p':
sprintf(s, "%p", va_arg(vargs, void*));
s += strlen(s);
break;
case '%':
*s++ = '%';
break;
default:
strcpy(s, p);
s += strlen(s);
goto end;
}
} else
*s++ = *f;
}
end:
_PyString_Resize(&string, s - PyString_AsString(string));
return string;
}
PyObject *
PyString_FromFormat(const char *format, ...)
{
va_list vargs;
#ifdef HAVE_STDARG_PROTOTYPES
va_start(vargs, format);
#else
va_start(vargs);
#endif
return PyString_FromFormatV(format, vargs);
}
PyObject *PyString_Decode(const char *s,
int size,
const char *encoding,