From dadace004b4b94dcc4437bafc9c8407fbb1bed74 Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Fri, 24 Aug 2001 18:32:06 +0000 Subject: 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. --- Include/stringobject.h | 4 ++ Objects/stringobject.c | 155 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 159 insertions(+) diff --git a/Include/stringobject.h b/Include/stringobject.h index 96f371e..2d9ed2d 100644 --- a/Include/stringobject.h +++ b/Include/stringobject.h @@ -7,6 +7,8 @@ extern "C" { #endif +#include + /* Type PyStringObject represents a character string. An extra zero byte is reserved at the end to ensure it is zero-terminated, but a size is @@ -53,6 +55,8 @@ extern DL_IMPORT(PyTypeObject) PyString_Type; extern DL_IMPORT(PyObject *) PyString_FromStringAndSize(const char *, int); extern DL_IMPORT(PyObject *) PyString_FromString(const char *); +extern DL_IMPORT(PyObject *) PyString_FromFormatV(const char*, va_list); +extern DL_IMPORT(PyObject *) PyString_FromFormat(const char*, ...); extern DL_IMPORT(int) PyString_Size(PyObject *); extern DL_IMPORT(char *) PyString_AsString(PyObject *); extern DL_IMPORT(void) PyString_Concat(PyObject **, PyObject *); diff --git a/Objects/stringobject.c b/Objects/stringobject.c index a8e063e..3acc69f 100644 --- a/Objects/stringobject.c +++ b/Objects/stringobject.c @@ -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, -- cgit v0.12