]> granicus.if.org Git - python/commitdiff
PyString_FromFormat() and PyString_FromFormatV(): Largely ripped from
authorBarry Warsaw <barry@python.org>
Fri, 24 Aug 2001 18:32:06 +0000 (18:32 +0000)
committerBarry Warsaw <barry@python.org>
Fri, 24 Aug 2001 18:32:06 +0000 (18:32 +0000)
    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
Objects/stringobject.c

index 96f371ee7edf27831717edbb22e666fe69a09059..2d9ed2d91c77a9aa3143ea8d909857bbd4dbf451 100644 (file)
@@ -7,6 +7,8 @@
 extern "C" {
 #endif
 
+#include <stdarg.h>
+
 /*
 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 *);
index a8e063e794318a1e3ababaa4203184b2ab3af963..3acc69f9b099025c03d8e4d3acdd81c188e3e03d 100644 (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,