Merged revisions 64984 via svnmerge from
svn+ssh://pythondev@svn.python.org/python/trunk

........
  r64984 | eric.smith | 2008-07-15 20:11:49 -0400 (Tue, 15 Jul 2008) | 1 line

  Complete issue 3083: add alternate (#) formatting to bin, oct, hex in str.format().
........
diff --git a/Objects/stringlib/formatter.h b/Objects/stringlib/formatter.h
index 9b7d607..ba43200 100644
--- a/Objects/stringlib/formatter.h
+++ b/Objects/stringlib/formatter.h
@@ -147,6 +147,13 @@
 #endif
     }
 
+    /* If the next character is #, we're in alternate mode.  This only
+       applies to integers. */
+    if (end-ptr >= 1 && ptr[0] == '#') {
+	format->alternate = 1;
+	++ptr;
+    }
+
     /* The special case for 0-padding (backwards compat) */
     if (format->fill_char == '\0' && end-ptr >= 1 && ptr[0] == '0') {
         format->fill_char = '0';
@@ -156,13 +163,6 @@
         ++ptr;
     }
 
-    /* If the next character is #, we're in alternate mode.  This only
-       applies to integers. */
-    if (end-ptr >= 1 && ptr[0] == '#') {
-	format->alternate = 1;
-	++ptr;
-    }
-
     /* XXX add error checking */
     specified_width = get_integer(&ptr, end, &format->width);
 
@@ -211,9 +211,10 @@
 /************************************************************************/
 
 /* describes the layout for an integer, see the comment in
-   _calc_integer_widths() for details */
+   calc_number_widths() for details */
 typedef struct {
     Py_ssize_t n_lpadding;
+    Py_ssize_t n_prefix;
     Py_ssize_t n_spadding;
     Py_ssize_t n_rpadding;
     char lsign;
@@ -234,6 +235,7 @@
 		   const InternalFormatSpec *format)
 {
     r->n_lpadding = 0;
+    r->n_prefix = 0;
     r->n_spadding = 0;
     r->n_rpadding = 0;
     r->lsign = '\0';
@@ -288,13 +290,16 @@
         }
     }
 
+    r->n_prefix = n_prefix;
+
     /* now the number of padding characters */
     if (format->width == -1) {
         /* no padding at all, nothing to do */
     }
     else {
         /* see if any padding is needed */
-        if (r->n_lsign + n_digits + r->n_rsign >= format->width) {
+        if (r->n_lsign + n_digits + r->n_rsign +
+	        r->n_prefix >= format->width) {
             /* no padding needed, we're already bigger than the
                requested width */
         }
@@ -302,7 +307,8 @@
             /* determine which of left, space, or right padding is
                needed */
             Py_ssize_t padding = format->width -
-		                    (r->n_lsign + n_digits + r->n_rsign);
+		                    (r->n_lsign + r->n_prefix +
+				     n_digits + r->n_rsign);
             if (format->align == '<')
                 r->n_rpadding = padding;
             else if (format->align == '>')
@@ -317,18 +323,19 @@
                 r->n_lpadding = padding;
         }
     }
-    r->n_total = r->n_lpadding + r->n_lsign + r->n_spadding +
-        n_digits + r->n_rsign + r->n_rpadding;
+    r->n_total = r->n_lpadding + r->n_lsign + r->n_prefix +
+	    r->n_spadding + n_digits + r->n_rsign + r->n_rpadding;
 }
 
 /* fill in the non-digit parts of a numbers's string representation,
-   as determined in _calc_integer_widths().  returns the pointer to
+   as determined in calc_number_widths().  returns the pointer to
    where the digits go. */
 static STRINGLIB_CHAR *
 fill_non_digits(STRINGLIB_CHAR *p_buf, const NumberFieldWidths *spec,
-		Py_ssize_t n_digits, STRINGLIB_CHAR fill_char)
+		STRINGLIB_CHAR *prefix, Py_ssize_t n_digits,
+		STRINGLIB_CHAR fill_char)
 {
-    STRINGLIB_CHAR* p_digits;
+    STRINGLIB_CHAR *p_digits;
 
     if (spec->n_lpadding) {
         STRINGLIB_FILL(p_buf, fill_char, spec->n_lpadding);
@@ -337,6 +344,12 @@
     if (spec->n_lsign == 1) {
         *p_buf++ = spec->lsign;
     }
+    if (spec->n_prefix) {
+	memmove(p_buf,
+		prefix,
+		spec->n_prefix * sizeof(STRINGLIB_CHAR));
+	p_buf += spec->n_prefix;
+    }
     if (spec->n_spadding) {
         STRINGLIB_FILL(p_buf, fill_char, spec->n_spadding);
         p_buf += spec->n_spadding;
@@ -477,6 +490,8 @@
     Py_ssize_t n_grouping_chars = 0; /* Count of additional chars to
 					allocate, used for 'n'
 					formatting. */
+    Py_ssize_t n_prefix = 0;   /* Count of prefix chars, (e.g., '0x') */
+    STRINGLIB_CHAR *prefix = NULL;
     NumberFieldWidths spec;
     long x;
 
@@ -534,19 +549,16 @@
         switch (format->type) {
         case 'b':
             base = 2;
-	    if (!format->alternate)
-		leading_chars_to_skip = 2; /* 0b */
+	    leading_chars_to_skip = 2; /* 0b */
             break;
         case 'o':
             base = 8;
-	    if (!format->alternate)
-		leading_chars_to_skip = 2; /* 0o */
+	    leading_chars_to_skip = 2; /* 0o */
             break;
         case 'x':
         case 'X':
             base = 16;
-	    if (!format->alternate)
-		leading_chars_to_skip = 2; /* 0x */
+	    leading_chars_to_skip = 2; /* 0x */
             break;
         default:  /* shouldn't be needed, but stops a compiler warning */
         case 'd':
@@ -555,6 +567,11 @@
             break;
         }
 
+	/* The number of prefix chars is the same as the leading
+	   chars to skip */
+	if (format->alternate)
+	    n_prefix = leading_chars_to_skip;
+
         /* Do the hard part, converting to a string in a given base */
 	tmp = tostring(value, base);
         if (tmp == NULL)
@@ -563,6 +580,8 @@
 	pnumeric_chars = STRINGLIB_STR(tmp);
         n_digits = STRINGLIB_LEN(tmp);
 
+	prefix = pnumeric_chars;
+
 	/* Remember not to modify what pnumeric_chars points to.  it
 	   might be interned.  Only modify it after we copy it into a
 	   newly allocated output buffer. */
@@ -571,6 +590,7 @@
            and skip it */
         sign = pnumeric_chars[0];
         if (sign == '-') {
+	    ++prefix;
 	    ++leading_chars_to_skip;
         }
 
@@ -586,7 +606,8 @@
 			       0, &n_grouping_chars, 0);
 
     /* Calculate the widths of the various leading and trailing parts */
-    calc_number_widths(&spec, sign, 0, n_digits + n_grouping_chars, format);
+    calc_number_widths(&spec, sign, n_prefix, n_digits + n_grouping_chars,
+		       format);
 
     /* Allocate a new string to hold the result */
     result = STRINGLIB_NEW(NULL, spec.n_total);
@@ -594,35 +615,52 @@
 	goto done;
     p = STRINGLIB_STR(result);
 
+    /* XXX There is too much magic here regarding the internals of
+       spec and the location of the prefix and digits.  It would be
+       better if calc_number_widths returned a number of logical
+       offsets into the buffer, and those were used.  Maybe in a
+       future code cleanup. */
+
     /* Fill in the digit parts */
-    n_leading_chars = spec.n_lpadding + spec.n_lsign + spec.n_spadding;
+    n_leading_chars = spec.n_lpadding + spec.n_lsign +
+	    spec.n_prefix + spec.n_spadding;
     memmove(p + n_leading_chars,
 	    pnumeric_chars,
 	    n_digits * sizeof(STRINGLIB_CHAR));
 
-    /* If type is 'X', convert to uppercase */
+    /* If type is 'X', convert the filled in digits to uppercase */
     if (format->type == 'X') {
 	Py_ssize_t t;
 	for (t = 0; t < n_digits; ++t)
 	    p[t + n_leading_chars] = STRINGLIB_TOUPPER(p[t + n_leading_chars]);
     }
 
-    /* Insert the grouping, if any, after the uppercasing of 'X', so we can
-       ensure that grouping chars won't be affected. */
+    /* Insert the grouping, if any, after the uppercasing of the digits, so
+       we can ensure that grouping chars won't be affected. */
     if (n_grouping_chars) {
 	    /* We know this can't fail, since we've already
 	       reserved enough space. */
 	    STRINGLIB_CHAR *pstart = p + n_leading_chars;
 	    int r = STRINGLIB_GROUPING(pstart, n_digits, n_digits,
-				       spec.n_total+n_grouping_chars-n_leading_chars,
-				       NULL, 0);
+			   spec.n_total+n_grouping_chars-n_leading_chars,
+			   NULL, 0);
 	    assert(r);
     }
 
     /* Fill in the non-digit parts (padding, sign, etc.) */
-    fill_non_digits(p, &spec, n_digits + n_grouping_chars,
+    fill_non_digits(p, &spec, prefix, n_digits + n_grouping_chars,
 		    format->fill_char == '\0' ? ' ' : format->fill_char);
 
+    /* If type is 'X', uppercase the prefix.  This has to be done after the
+       prefix is filled in by fill_non_digits */
+    if (format->type == 'X') {
+	Py_ssize_t t;
+	for (t = 0; t < n_prefix; ++t)
+	    p[t + spec.n_lpadding + spec.n_lsign] =
+		    STRINGLIB_TOUPPER(p[t + spec.n_lpadding + spec.n_lsign]);
+    }
+
+
 done:
     Py_XDECREF(tmp);
     return result;
@@ -768,7 +806,7 @@
         goto done;
 
     /* Fill in the non-digit parts (padding, sign, etc.) */
-    fill_non_digits(STRINGLIB_STR(result), &spec, n_digits,
+    fill_non_digits(STRINGLIB_STR(result), &spec, NULL, n_digits,
 		    format->fill_char == '\0' ? ' ' : format->fill_char);
 
     /* fill in the digit parts */