[macruby-changes] [3488] MacRuby/trunk

source_changes at macosforge.org source_changes at macosforge.org
Wed Feb 10 18:40:28 PST 2010


Revision: 3488
          http://trac.macosforge.org/projects/ruby/changeset/3488
Author:   lsansonetti at apple.com
Date:     2010-02-10 18:40:26 -0800 (Wed, 10 Feb 2010)
Log Message:
-----------
sprintf can now be free of C++ evil

Added Paths:
-----------
    MacRuby/trunk/sprintf.c

Removed Paths:
-------------
    MacRuby/trunk/sprintf.cpp

Copied: MacRuby/trunk/sprintf.c (from rev 3487, MacRuby/trunk/sprintf.cpp)
===================================================================
--- MacRuby/trunk/sprintf.c	                        (rev 0)
+++ MacRuby/trunk/sprintf.c	2010-02-11 02:40:26 UTC (rev 3488)
@@ -0,0 +1,789 @@
+/*
+ * MacRuby implementation of Ruby 1.9's sprintf.c.
+ *
+ * This file is covered by the Ruby license. See COPYING for more details.
+ *
+ * Copyright (C) 2007-2010, Apple Inc. All rights reserved.
+ * Copyright (C) 1993-2007 Yukihiro Matsumoto
+ * Copyright (C) 2000  Network Applied Communication Laboratory, Inc.
+ * Copyright (C) 2000  Information-technology Promotion Agency, Japan
+ */
+
+#include <stdarg.h>
+
+#include "ruby/ruby.h"
+#include "ruby/node.h"
+#include "ruby/encoding.h"
+#include "vm.h"
+#include "compiler.h"
+
+/*
+ *  call-seq:
+ *     format(format_string [, arguments...] )   => string
+ *     sprintf(format_string [, arguments...] )  => string
+ *  
+ *  Returns the string resulting from applying <i>format_string</i> to
+ *  any additional arguments.  Within the format string, any characters
+ *  other than format sequences are copied to the result. 
+ *
+ *  The syntax of a format sequence is follows.
+ *
+ *    %[flags][width][.precision]type
+ *
+ *  A format
+ *  sequence consists of a percent sign, followed by optional flags,
+ *  width, and precision indicators, then terminated with a field type
+ *  character.  The field type controls how the corresponding
+ *  <code>sprintf</code> argument is to be interpreted, while the flags
+ *  modify that interpretation.
+ *
+ *  The field type characters are:
+ *
+ *      Field |  Integer Format
+ *      ------+--------------------------------------------------------------
+ *        b   | Convert argument as a binary number.
+ *            | Negative numbers will be displayed as a two's complement
+ *            | prefixed with `..1'.
+ *        B   | Equivalent to `b', but uses an uppercase 0B for prefix
+ *            | in the alternative format by #.
+ *        d   | Convert argument as a decimal number.
+ *        i   | Identical to `d'.
+ *        o   | Convert argument as an octal number.
+ *            | Negative numbers will be displayed as a two's complement
+ *            | prefixed with `..7'.
+ *        u   | Identical to `d'.
+ *        x   | Convert argument as a hexadecimal number.
+ *            | Negative numbers will be displayed as a two's complement
+ *            | prefixed with `..f' (representing an infinite string of
+ *            | leading 'ff's).
+ *        X   | Equivalent to `x', but uses uppercase letters.
+ *
+ *      Field |  Float Format
+ *      ------+--------------------------------------------------------------
+ *        e   | Convert floating point argument into exponential notation 
+ *            | with one digit before the decimal point as [-]d.dddddde[+-]dd.
+ *            | The precision specifies the number of digits after the decimal
+ *            | point (defaulting to six).
+ *        E   | Equivalent to `e', but uses an uppercase E to indicate
+ *            | the exponent.
+ *        f   | Convert floating point argument as [-]ddd.dddddd, 
+ *            | where the precision specifies the number of digits after
+ *            | the decimal point.
+ *        g   | Convert a floating point number using exponential form
+ *            | if the exponent is less than -4 or greater than or
+ *            | equal to the precision, or in dd.dddd form otherwise.
+ *            | The precision specifies the number of significant digits.
+ *        G   | Equivalent to `g', but use an uppercase `E' in exponent form.
+ *
+ *      Field |  Other Format
+ *      ------+--------------------------------------------------------------
+ *        c   | Argument is the numeric code for a single character or
+ *            | a single character string itself.
+ *        p   | The valuing of argument.inspect.
+ *        s   | Argument is a string to be substituted.  If the format
+ *            | sequence contains a precision, at most that many characters
+ *            | will be copied.
+ *     
+ *  The flags modifies the behavior of the formats.
+ *  The flag characters are:
+ *
+ *    Flag     | Applies to    | Meaning
+ *    ---------+---------------+-----------------------------------------
+ *    space    | bBdiouxX      | Leave a space at the start of 
+ *             | eEfgG         | non-negative numbers.
+ *             | (numeric fmt) | For `o', `x', `X', `b' and `B', use
+ *             |               | a minus sign with absolute value for
+ *             |               | negative values.
+ *    ---------+---------------+-----------------------------------------
+ *    (digit)$ | all           | Specifies the absolute argument number
+ *             |               | for this field.  Absolute and relative
+ *             |               | argument numbers cannot be mixed in a
+ *             |               | sprintf string.
+ *    ---------+---------------+-----------------------------------------
+ *     #       | bBoxX         | Use an alternative format.
+ *             | eEfgG         | For the conversions `o', increase the precision
+ *             |               | until the first digit will be `0' if
+ *             |               | it is not formatted as complements.
+ *             |               | For the conversions `x', `X', `b' and `B'
+ *             |               | on non-zero, prefix the result with ``0x'',
+ *             |               | ``0X'', ``0b'' and ``0B'', respectively.
+ *             |               | For `e', `E', `f', `g', and 'G',
+ *             |               | force a decimal point to be added,
+ *             |               | even if no digits follow.
+ *             |               | For `g' and 'G', do not remove trailing zeros.
+ *    ---------+---------------+-----------------------------------------
+ *    +        | bBdiouxX      | Add a leading plus sign to non-negative
+ *             | eEfgG         | numbers.
+ *             | (numeric fmt) | For `o', `x', `X', `b' and `B', use
+ *             |               | a minus sign with absolute value for
+ *             |               | negative values.
+ *    ---------+---------------+-----------------------------------------
+ *    -        | all           | Left-justify the result of this conversion.
+ *    ---------+---------------+-----------------------------------------
+ *    0 (zero) | bBdiouxX      | Pad with zeros, not spaces.
+ *             | eEfgG         | For `o', `x', `X', `b' and `B', radix-1
+ *             | (numeric fmt) | is used for negative numbers formatted as
+ *             |               | complements.
+ *    ---------+---------------+-----------------------------------------
+ *    *        | all           | Use the next argument as the field width. 
+ *             |               | If negative, left-justify the result. If the
+ *             |               | asterisk is followed by a number and a dollar 
+ *             |               | sign, use the indicated argument as the width.
+ *
+ *  Examples of flags:
+ *
+ *   # `+' and space flag specifies the sign of non-negative numbers.
+ *   sprintf("%d", 123)  #=> "123"
+ *   sprintf("%+d", 123) #=> "+123"
+ *   sprintf("% d", 123) #=> " 123"
+ *
+ *   # `#' flag for `o' increases number of digits to show `0'.
+ *   # `+' and space flag changes format of negative numbers.
+ *   sprintf("%o", 123)   #=> "173"
+ *   sprintf("%#o", 123)  #=> "0173"
+ *   sprintf("%+o", -123) #=> "-173"
+ *   sprintf("%o", -123)  #=> "..7605"
+ *   sprintf("%#o", -123) #=> "..7605"
+ *
+ *   # `#' flag for `x' add a prefix `0x' for non-zero numbers.
+ *   # `+' and space flag disables complements for negative numbers.
+ *   sprintf("%x", 123)   #=> "7b"
+ *   sprintf("%#x", 123)  #=> "0x7b"
+ *   sprintf("%+x", -123) #=> "-7b"
+ *   sprintf("%x", -123)  #=> "..f85"
+ *   sprintf("%#x", -123) #=> "0x..f85"
+ *   sprintf("%#x", 0)    #=> "0"
+ *
+ *   # `#' for `X' uses the prefix `0X'.
+ *   sprintf("%X", 123)  #=> "7B"
+ *   sprintf("%#X", 123) #=> "0X7B"
+ *
+ *   # `#' flag for `b' add a prefix `0b' for non-zero numbers.
+ *   # `+' and space flag disables complements for negative numbers.
+ *   sprintf("%b", 123)   #=> "1111011"
+ *   sprintf("%#b", 123)  #=> "0b1111011"
+ *   sprintf("%+b", -123) #=> "-1111011"
+ *   sprintf("%b", -123)  #=> "..10000101"
+ *   sprintf("%#b", -123) #=> "0b..10000101"
+ *   sprintf("%#b", 0)    #=> "0"
+ *
+ *   # `#' for `B' uses the prefix `0B'.
+ *   sprintf("%B", 123)  #=> "1111011"
+ *   sprintf("%#B", 123) #=> "0B1111011"
+ *
+ *   # `#' for `e' forces to show the decimal point.
+ *   sprintf("%.0e", 1)  #=> "1e+00"
+ *   sprintf("%#.0e", 1) #=> "1.e+00"
+ *
+ *   # `#' for `f' forces to show the decimal point.
+ *   sprintf("%.0f", 1234)  #=> "1234"
+ *   sprintf("%#.0f", 1234) #=> "1234."
+ *
+ *   # `#' for `g' forces to show the decimal point.
+ *   # It also disables stripping lowest zeros.
+ *   sprintf("%g", 123.4)   #=> "123.4"
+ *   sprintf("%#g", 123.4)  #=> "123.400"
+ *   sprintf("%g", 123456)  #=> "123456"
+ *   sprintf("%#g", 123456) #=> "123456."
+ *     
+ *  The field width is an optional integer, followed optionally by a
+ *  period and a precision.  The width specifies the minimum number of
+ *  characters that will be written to the result for this field.
+ *
+ *  Examples of width:
+ *
+ *   # padding is done by spaces,       width=20
+ *   # 0 or radix-1.             <------------------>
+ *   sprintf("%20d", 123)   #=> "                 123"
+ *   sprintf("%+20d", 123)  #=> "                +123"
+ *   sprintf("%020d", 123)  #=> "00000000000000000123"
+ *   sprintf("%+020d", 123) #=> "+0000000000000000123"
+ *   sprintf("% 020d", 123) #=> " 0000000000000000123"
+ *   sprintf("%-20d", 123)  #=> "123                 "
+ *   sprintf("%-+20d", 123) #=> "+123                "
+ *   sprintf("%- 20d", 123) #=> " 123                "
+ *   sprintf("%020x", -123) #=> "..ffffffffffffffff85"
+ *
+ *  For
+ *  numeric fields, the precision controls the number of decimal places
+ *  displayed.  For string fields, the precision determines the maximum
+ *  number of characters to be copied from the string.  (Thus, the format
+ *  sequence <code>%10.10s</code> will always contribute exactly ten
+ *  characters to the result.)
+ *
+ *  Examples of precisions:
+ *
+ *   # precision for `d', 'o', 'x' and 'b' is
+ *   # minimum number of digits               <------>
+ *   sprintf("%20.8d", 123)  #=> "            00000123"
+ *   sprintf("%20.8o", 123)  #=> "            00000173"
+ *   sprintf("%20.8x", 123)  #=> "            0000007b"
+ *   sprintf("%20.8b", 123)  #=> "            01111011"
+ *   sprintf("%20.8d", -123) #=> "           -00000123"
+ *   sprintf("%20.8o", -123) #=> "            ..777605"
+ *   sprintf("%20.8x", -123) #=> "            ..ffff85"
+ *   sprintf("%20.8b", -11)  #=> "            ..110101"
+ *
+ *   # "0x" and "0b" for `#x' and `#b' is not counted for
+ *   # precision but "0" for `#o' is counted.  <------>
+ *   sprintf("%#20.8d", 123)  #=> "            00000123"
+ *   sprintf("%#20.8o", 123)  #=> "            00000173"
+ *   sprintf("%#20.8x", 123)  #=> "          0x0000007b"
+ *   sprintf("%#20.8b", 123)  #=> "          0b01111011"
+ *   sprintf("%#20.8d", -123) #=> "           -00000123"
+ *   sprintf("%#20.8o", -123) #=> "            ..777605"
+ *   sprintf("%#20.8x", -123) #=> "          0x..ffff85"
+ *   sprintf("%#20.8b", -11)  #=> "          0b..110101"
+ *
+ *   # precision for `e' is number of
+ *   # digits after the decimal point           <------>
+ *   sprintf("%20.8e", 1234.56789) #=> "      1.23456789e+03"
+ *                                    
+ *   # precision for `f' is number of
+ *   # digits after the decimal point               <------>
+ *   sprintf("%20.8f", 1234.56789) #=> "       1234.56789000"
+ *
+ *   # precision for `g' is number of
+ *   # significant digits                          <------->
+ *   sprintf("%20.8g", 1234.56789) #=> "           1234.5679"
+ *
+ *   #                                         <------->
+ *   sprintf("%20.8g", 123456789)  #=> "       1.2345679e+08"
+ *
+ *   # precision for `s' is
+ *   # maximum number of characters                    <------>
+ *   sprintf("%20.8s", "string test") #=> "            string t"
+ *     
+ *  Examples:
+ *
+ *     sprintf("%d %04x", 123, 123)               #=> "123 007b"
+ *     sprintf("%08b '%4s'", 123, 123)            #=> "01111011 ' 123'"
+ *     sprintf("%1$*2$s %2$d %1$s", "hello", 8)   #=> "   hello 8 hello"
+ *     sprintf("%1$*2$s %2$d", "hello", -8)       #=> "hello    -8"
+ *     sprintf("%+g:% g:%-g", 1.23, 1.23, 1.23)   #=> "+1.23: 1.23:1.23"
+ *     sprintf("%u", -123)                        #=> "-123"
+ */
+
+#define GETNTHARG(nth) \
+    ((nth >= argc) ? (rb_raise(rb_eArgError, "too few arguments"), 0) : \
+    argv[nth])
+
+VALUE
+rb_f_sprintf_imp(VALUE recv, SEL sel, int argc, VALUE *argv)
+{
+    return rb_str_format(argc - 1, argv + 1, GETNTHARG(0));
+}
+
+VALUE
+rb_f_sprintf(int argc, const VALUE *argv)
+{
+    return rb_str_format(argc - 1, argv + 1, GETNTHARG(0));
+}
+
+VALUE
+rb_enc_vsprintf(rb_encoding *enc, const char *fmt, va_list ap)
+{
+    char buffer[512];
+    int n;
+    n = vsnprintf(buffer, sizeof buffer, fmt, ap);
+    return rb_enc_str_new(buffer, n, enc);
+}
+
+VALUE
+rb_enc_sprintf(rb_encoding *enc, const char *format, ...)
+{
+    VALUE result;
+    va_list ap;
+
+    va_start(ap, format);
+    result = rb_enc_vsprintf(enc, format, ap);
+    va_end(ap);
+
+    return result;
+}
+
+VALUE
+rb_vsprintf(const char *fmt, va_list ap)
+{
+    return rb_enc_vsprintf(NULL, fmt, ap);
+}
+
+VALUE
+rb_sprintf(const char *format, ...)
+{
+    VALUE result;
+    va_list ap;
+
+    va_start(ap, format);
+    result = rb_vsprintf(format, ap);
+    va_end(ap);
+
+    return result;
+}
+
+#define IS_NEG(num) RBIGNUM_NEGATIVE_P(num)
+#define REL_REF	    1
+#define ABS_REF	    2
+#define NAMED_REF   3
+
+#define REF_NAME(type) \
+    ((type) == REL_REF ? "relative" : (type) == ABS_REF ? "absolute" : "named")
+
+#define SET_REF_TYPE(type) \
+    if (ref_type != 0 && (type) != ref_type) { \
+	rb_raise(rb_eArgError, "can't mix %s references with %s references", \
+		REF_NAME(type), REF_NAME(ref_type)); \
+    } \
+    ref_type = (type);
+
+#define GET_ARG() \
+    if (arg == 0) { \
+	SET_REF_TYPE(REL_REF); \
+	arg = GETNTHARG(j); \
+	j++; \
+    }
+    
+#define isprenum(ch) ((ch) == '-' || (ch) == ' ' || (ch) == '+')
+
+static void
+pad_format_value(VALUE arg, long start, long width,
+	CFStringRef pad)
+{
+    long slen = (long)CFStringGetLength((CFStringRef)arg);
+    if (width <= slen) {
+	return;
+    }
+    if (start < 0) {
+	start += slen + 1;
+    }
+    width -= slen;
+    do {
+	CFStringInsert((CFMutableStringRef)arg, start, pad);
+    } while (--width > 0);
+}
+
+static long
+cstr_update(char **str, unsigned long start, unsigned long num, char *replace)
+{
+    unsigned long len = strlen(*str) + 1;
+    unsigned long replace_len = strlen(replace);
+    if (start + num > len) {
+	num = len - start;
+    }
+    if (replace_len >= num) {
+	char *new_str = (char *)xmalloc(len + replace_len - num);
+	memcpy(new_str, *str, len);
+	*str = new_str;
+    }
+    if (replace_len != num) {
+	bcopy(*str + start + num, *str + start + replace_len, len - start -
+		num);
+    }
+    if (replace_len > 0) {
+	bcopy(replace, *str + start, replace_len);
+    }
+    return replace_len - num;
+}
+
+VALUE
+get_named_arg(char *format_str, unsigned long format_len, unsigned long *i,
+	VALUE hash)
+{
+    if (TYPE(hash) != T_HASH) {
+	rb_raise(rb_eArgError,
+		 "hash required for named references");
+    }
+    char closing = format_str[(*i)++] + 2;
+    char *str_ptr = format_str + *i;
+    while (*i < format_len && format_str[*i] != closing) {
+	(*i)++;
+    }
+    if (*i == format_len) {
+	rb_raise(rb_eArgError,
+		 "malformed name - unmatched parenthesis");
+    }
+    format_str[*i] = '\0';
+    hash = rb_hash_aref(hash, rb_name2sym(str_ptr));
+    format_str[*i] = closing;
+    return (hash);
+}
+
+// XXX
+// - this method uses strtol to read numbers from the format string, so
+//   extremely large numbers get silently truncated. this should be fixed
+// - switch to a cfstring format string to allow for proper encoding support
+    
+// XXX look for arguments that are altered but not duped
+VALUE
+rb_str_format(int argc, const VALUE *argv, VALUE fmt)
+{
+    bool tainted = OBJ_TAINTED(fmt);
+    fmt = rb_str_new3(fmt);
+    char *format_str = (char *)RSTRING_PTR(fmt);
+    unsigned long format_len = strlen(format_str);
+    long num;
+    int j = 0;
+    int ref_type = 0;
+
+    for (unsigned long i = 0; i < format_len; i++) {
+	if (format_str[i] != '%') {
+	    continue;
+	}
+	if (format_str[i + 1] == '%') {
+	    cstr_update(&format_str, i, 1, (char *)"");
+	    continue;
+	}
+
+	bool sharp_flag = false;
+	bool space_flag = false;
+	bool plus_flag = false;
+	bool minus_flag = false;
+	bool zero_flag = false;
+	bool precision_flag = false;
+	bool complete = false;
+	VALUE arg = 0;
+	long width = 0;
+	long precision = 0;
+	int base = 0;
+	CFStringRef negative_pad = NULL;
+	CFStringRef sharp_pad = CFSTR("");
+	char *str_ptr;
+
+	unsigned long start = i;
+	while (i++ < format_len) {
+	    switch (format_str[i]) {
+		case '#':
+		    sharp_flag = true;
+		    break;
+
+		case '*':
+		    if (format_str[++i] == '<' || format_str[i] == '{') {
+			SET_REF_TYPE(NAMED_REF);
+			width = NUM2LONG(rb_Integer(get_named_arg(format_str,
+				format_len, &i, GETNTHARG(0))));
+		    }
+		    else {
+			if (isprenum(format_str[i])) {
+			    i--;
+			    break;
+			}
+			num = strtol(format_str + i, &str_ptr, 10);
+			if (str_ptr == format_str + i--) {
+			    SET_REF_TYPE(REL_REF);
+			    width = NUM2LONG(rb_Integer(GETNTHARG(j)));
+			    j++;
+			}
+			else if (*str_ptr == '$') {
+			    SET_REF_TYPE(ABS_REF);
+			    width = NUM2LONG(rb_Integer(GETNTHARG(num - 1)));
+			    i = str_ptr - format_str;
+			}
+		    }
+		    if (width < 0) {
+			minus_flag = true;
+			width = -width;
+		    }
+		    break;
+
+		case ' ':
+		    if (!plus_flag) {
+			space_flag = true;
+		    }
+		    break;
+
+		case '+':
+		    plus_flag = true;
+		    space_flag = false;
+		    break;
+
+		case '-':
+		    zero_flag = false;
+		    minus_flag = true;
+		    break;
+
+		case '0':
+		    if (!precision_flag && !minus_flag) {
+			zero_flag = true;
+		    }
+		    break;
+
+		case '1':
+		case '2':
+		case '3':
+		case '4':
+		case '5':
+		case '6':
+		case '7':
+		case '8':
+		case '9':
+		    num = strtol(format_str + i, &str_ptr, 10);
+		    i = str_ptr - format_str;
+		    if (*str_ptr == '$') {
+			if (num == 0) {
+			    rb_raise(rb_eArgError, "invalid absolute argument");
+			}
+			SET_REF_TYPE(ABS_REF);
+			arg = GETNTHARG(num - 1);
+		    }
+		    else {
+			SET_REF_TYPE(REL_REF);
+			width = num;
+			i--;
+		    }
+		    break;
+
+		case '.':
+		    zero_flag = false;
+		    precision_flag = true;
+		    if (format_str[++i] == '*') {
+			if (format_str[++i] == '<' || format_str[i] == '{') {
+			    SET_REF_TYPE(NAMED_REF);
+			    precision = NUM2LONG(rb_Integer(get_named_arg(
+				    format_str, format_len, &i, GETNTHARG(0))));
+			}
+			else {
+			    if (isprenum(format_str[i])) {
+				i--;
+				break;
+			    }
+			    num = strtol(format_str + i, &str_ptr, 10);
+			    if (str_ptr == format_str + i--) {
+				SET_REF_TYPE(REL_REF);
+				precision = NUM2LONG(rb_Integer(GETNTHARG(j)));
+				j++;
+			    }
+			    else if (*str_ptr == '$') {
+				SET_REF_TYPE(ABS_REF);
+				precision = NUM2LONG(rb_Integer(GETNTHARG(
+					num - 1)));
+				i = str_ptr - format_str;
+			    }
+			}
+		    }
+		    else if (isdigit(format_str[i])) {
+			precision = strtol(format_str + i, &str_ptr, 10);
+			i = str_ptr - format_str - 1;
+		    }
+		    else {
+			rb_raise(rb_eArgError, "invalid precision");
+		    }
+
+		    if (precision < 0) {
+			precision = 0;
+		    }
+		    break;
+
+		case '<':
+		case '{':
+		    SET_REF_TYPE(NAMED_REF);
+		    arg = get_named_arg(format_str, format_len, &i,
+			    GETNTHARG(0));
+		    break;
+
+		case 'd':
+		case 'D':
+		case 'i':
+		case 'u':
+		case 'U':
+		    base = 10;
+		    complete = true;
+		    break;
+
+		case 'x':
+		case 'X':
+		    base = 16;
+		    negative_pad = CFSTR("f");
+		    sharp_pad = CFSTR("0x");
+		    complete = true;
+		    break;
+
+		case 'o':
+		case 'O':
+		    base = 8;
+		    negative_pad = CFSTR("7");
+		    sharp_pad = CFSTR("0");
+		    complete = true;
+		    break;
+
+		case 'B':
+		case 'b':
+		    base = 2;
+		    negative_pad = CFSTR("1");
+		    sharp_pad = CFSTR("0b");
+		    complete = true;
+		    break;
+
+		case 'c':
+		case 'C':
+		    GET_ARG();
+		    if (TYPE(arg) == T_STRING) {
+			arg = rb_str_substr(arg, 0, 1);
+		    }
+		    else {
+			// rb_num_to_chr is broken so leave out the
+			// enc or we don't get range checking
+			arg = rb_num_to_chr(arg, NULL /*rb_enc_get(fmt)*/);
+		    }
+		    complete = true;
+		    break;
+
+		case 'f':
+		case 'F':
+		case 'e':
+		case 'E':
+		case 'g':
+		case 'G':
+		case 'a':
+		case 'A':
+		{
+		    // here we construct a new format str and then use
+		    // c's sprintf. why? because floats are retarded
+		    GET_ARG();
+		    double value = RFLOAT_VALUE(rb_Float(arg));
+		    complete = true;
+		    
+		    if (isnan(value) || isinf(value)) {
+			arg = rb_str_new2((char *)(isnan(value) ? "NaN" :
+				value < 0 ? "-Inf" : "Inf"));
+			if (isnan(value) || value > 0) {
+			    if (plus_flag) {
+				rb_str_update(arg, 0, 0, (VALUE)CFSTR("+"));
+			    }
+			    else if (space_flag) {
+				rb_str_update(arg, 0, 0, (VALUE)CFSTR(" "));
+			    }
+			}
+			break;
+		    }
+
+		    arg = rb_str_new(format_str + i, 1);
+		    if (precision_flag) {
+			rb_str_update(arg, 0, 0, rb_big2str(LONG2NUM(precision),
+				10));
+			rb_str_update(arg, 0, 0, (VALUE)CFSTR("."));
+		    }
+		    rb_str_update(arg, 0, 0, rb_big2str(LONG2NUM(width), 10));
+		    if (minus_flag) {
+			rb_str_update(arg, 0, 0, (VALUE)CFSTR("-"));
+		    }
+		    else if (zero_flag) {
+			rb_str_update(arg, 0, 0, (VALUE)CFSTR("0"));
+		    }
+		    if (plus_flag) {
+			rb_str_update(arg, 0, 0, (VALUE)CFSTR("+"));
+		    }
+		    else if (space_flag) {
+			rb_str_update(arg, 0, 0, (VALUE)CFSTR(" "));
+		    }
+		    if (sharp_flag) {
+			rb_str_update(arg, 0, 0, (VALUE)CFSTR("#"));
+		    }
+		    rb_str_update(arg, 0, 0, (VALUE)CFSTR("%"));
+
+		    asprintf(&str_ptr, RSTRING_PTR(arg), value);
+		    arg = rb_str_new2(str_ptr);
+		    free(str_ptr);
+		    break;
+		}
+
+		case 's':
+		case 'S':
+		case 'p':
+		case '@':
+		    GET_ARG();
+		    arg = (tolower(format_str[i]) != 's' ? rb_inspect(arg)
+			    : TYPE(arg) == T_STRING ? rb_str_new3(arg)
+			    : rb_obj_as_string(arg));
+		    if (precision_flag && precision
+			    < CFStringGetLength((CFStringRef)arg)) {
+			CFStringPad((CFMutableStringRef)arg, NULL, precision,
+				0);
+		    }
+		    complete = true;
+		    break;
+
+		default:
+		    rb_raise(rb_eArgError, "malformed format string - %%%c",
+			    format_str[i]);
+	    }
+	    if (!complete) {
+		continue;
+	    }
+
+	    GET_ARG();
+
+	    if (base != 0) {
+		bool sign_pad = false;
+		unsigned long num_index = 0;
+		CFStringRef zero_pad = CFSTR("0");
+
+		VALUE num = rb_Integer(arg);
+		if (TYPE(num) == T_FIXNUM) {
+		    num = rb_int2big(FIX2LONG(num));
+		}
+		if (plus_flag || space_flag) {
+		    sign_pad = 1;
+		}
+		if (IS_NEG(num)) {
+		    num_index = 1;
+		    if (!sign_pad && negative_pad != NULL) {
+			zero_pad = negative_pad;
+			num = rb_big_clone(num);
+			rb_big_2comp(num);
+		    }
+		}
+
+		arg = rb_big2str(num, base);
+		if (!sign_pad && IS_NEG(num) && negative_pad != NULL) {
+		    char neg = *RSTRING_PTR(negative_pad);
+		    str_ptr = (char *)RSTRING_PTR(arg) + 1;
+		    if (base == 8) {
+			*str_ptr |= ((~0 << 3) >> ((3 * strlen(str_ptr)) %
+				(sizeof(BDIGIT) * 8))) & ~(~0 << 3);
+		    }
+		    while (*str_ptr++ == neg) {
+			num_index++;
+		    }
+		    rb_str_update(arg, 0, num_index, (VALUE)negative_pad);
+		    rb_str_update(arg, 0, 0, (VALUE)CFSTR(".."));
+		    num_index = 2;
+		}
+
+		if (precision_flag) {
+		    pad_format_value(arg, num_index, precision + (IS_NEG(num) &&
+			    (sign_pad || negative_pad == NULL) ? 1 : 0),
+			    zero_pad);
+		}
+		if (sharp_flag && rb_cmpint(num, Qfalse, Qfalse) != 0) {
+		    rb_str_update(arg, sign_pad, 0, (VALUE)sharp_pad);
+		    num_index += 2;
+		}
+		if (sign_pad && RBIGNUM_POSITIVE_P(num)) {
+		    rb_str_update(arg, 0, 0, (VALUE)(plus_flag ?
+			    CFSTR("+") : CFSTR(" ")));
+		    num_index++;
+		}
+		if (zero_flag) {
+		    pad_format_value(arg, num_index, width, zero_pad);
+		}
+		if (ISUPPER(format_str[i])) {
+		    CFStringUppercase((CFMutableStringRef)arg, NULL);
+		}
+	    }
+	    
+	    if (OBJ_TAINTED(arg)) {
+		tainted = true;
+	    }
+
+	    pad_format_value(arg, minus_flag ? -1 : 0, width, CFSTR(" "));
+	    num = cstr_update(&format_str, start, i - start + 1,
+		    (char *)RSTRING_PTR(arg));
+	    format_len += num;
+	    i += num;
+	    break;
+	}
+    }
+
+    fmt = rb_str_new2(format_str);
+    return tainted ? OBJ_TAINT(fmt) : fmt;
+}

Deleted: MacRuby/trunk/sprintf.cpp
===================================================================
--- MacRuby/trunk/sprintf.cpp	2010-02-11 02:36:16 UTC (rev 3487)
+++ MacRuby/trunk/sprintf.cpp	2010-02-11 02:40:26 UTC (rev 3488)
@@ -1,797 +0,0 @@
-/*
- * MacRuby implementation of Ruby 1.9's sprintf.c.
- *
- * This file is covered by the Ruby license. See COPYING for more details.
- *
- * Copyright (C) 2007-2010, Apple Inc. All rights reserved.
- * Copyright (C) 1993-2007 Yukihiro Matsumoto
- * Copyright (C) 2000  Network Applied Communication Laboratory, Inc.
- * Copyright (C) 2000  Information-technology Promotion Agency, Japan
- */
-
-#include <stdarg.h>
-
-#include "llvm.h"
-#include "ruby/ruby.h"
-#include "ruby/node.h"
-#include "ruby/encoding.h"
-#include "vm.h"
-#include "compiler.h"
-
-/*
- *  call-seq:
- *     format(format_string [, arguments...] )   => string
- *     sprintf(format_string [, arguments...] )  => string
- *  
- *  Returns the string resulting from applying <i>format_string</i> to
- *  any additional arguments.  Within the format string, any characters
- *  other than format sequences are copied to the result. 
- *
- *  The syntax of a format sequence is follows.
- *
- *    %[flags][width][.precision]type
- *
- *  A format
- *  sequence consists of a percent sign, followed by optional flags,
- *  width, and precision indicators, then terminated with a field type
- *  character.  The field type controls how the corresponding
- *  <code>sprintf</code> argument is to be interpreted, while the flags
- *  modify that interpretation.
- *
- *  The field type characters are:
- *
- *      Field |  Integer Format
- *      ------+--------------------------------------------------------------
- *        b   | Convert argument as a binary number.
- *            | Negative numbers will be displayed as a two's complement
- *            | prefixed with `..1'.
- *        B   | Equivalent to `b', but uses an uppercase 0B for prefix
- *            | in the alternative format by #.
- *        d   | Convert argument as a decimal number.
- *        i   | Identical to `d'.
- *        o   | Convert argument as an octal number.
- *            | Negative numbers will be displayed as a two's complement
- *            | prefixed with `..7'.
- *        u   | Identical to `d'.
- *        x   | Convert argument as a hexadecimal number.
- *            | Negative numbers will be displayed as a two's complement
- *            | prefixed with `..f' (representing an infinite string of
- *            | leading 'ff's).
- *        X   | Equivalent to `x', but uses uppercase letters.
- *
- *      Field |  Float Format
- *      ------+--------------------------------------------------------------
- *        e   | Convert floating point argument into exponential notation 
- *            | with one digit before the decimal point as [-]d.dddddde[+-]dd.
- *            | The precision specifies the number of digits after the decimal
- *            | point (defaulting to six).
- *        E   | Equivalent to `e', but uses an uppercase E to indicate
- *            | the exponent.
- *        f   | Convert floating point argument as [-]ddd.dddddd, 
- *            | where the precision specifies the number of digits after
- *            | the decimal point.
- *        g   | Convert a floating point number using exponential form
- *            | if the exponent is less than -4 or greater than or
- *            | equal to the precision, or in dd.dddd form otherwise.
- *            | The precision specifies the number of significant digits.
- *        G   | Equivalent to `g', but use an uppercase `E' in exponent form.
- *
- *      Field |  Other Format
- *      ------+--------------------------------------------------------------
- *        c   | Argument is the numeric code for a single character or
- *            | a single character string itself.
- *        p   | The valuing of argument.inspect.
- *        s   | Argument is a string to be substituted.  If the format
- *            | sequence contains a precision, at most that many characters
- *            | will be copied.
- *     
- *  The flags modifies the behavior of the formats.
- *  The flag characters are:
- *
- *    Flag     | Applies to    | Meaning
- *    ---------+---------------+-----------------------------------------
- *    space    | bBdiouxX      | Leave a space at the start of 
- *             | eEfgG         | non-negative numbers.
- *             | (numeric fmt) | For `o', `x', `X', `b' and `B', use
- *             |               | a minus sign with absolute value for
- *             |               | negative values.
- *    ---------+---------------+-----------------------------------------
- *    (digit)$ | all           | Specifies the absolute argument number
- *             |               | for this field.  Absolute and relative
- *             |               | argument numbers cannot be mixed in a
- *             |               | sprintf string.
- *    ---------+---------------+-----------------------------------------
- *     #       | bBoxX         | Use an alternative format.
- *             | eEfgG         | For the conversions `o', increase the precision
- *             |               | until the first digit will be `0' if
- *             |               | it is not formatted as complements.
- *             |               | For the conversions `x', `X', `b' and `B'
- *             |               | on non-zero, prefix the result with ``0x'',
- *             |               | ``0X'', ``0b'' and ``0B'', respectively.
- *             |               | For `e', `E', `f', `g', and 'G',
- *             |               | force a decimal point to be added,
- *             |               | even if no digits follow.
- *             |               | For `g' and 'G', do not remove trailing zeros.
- *    ---------+---------------+-----------------------------------------
- *    +        | bBdiouxX      | Add a leading plus sign to non-negative
- *             | eEfgG         | numbers.
- *             | (numeric fmt) | For `o', `x', `X', `b' and `B', use
- *             |               | a minus sign with absolute value for
- *             |               | negative values.
- *    ---------+---------------+-----------------------------------------
- *    -        | all           | Left-justify the result of this conversion.
- *    ---------+---------------+-----------------------------------------
- *    0 (zero) | bBdiouxX      | Pad with zeros, not spaces.
- *             | eEfgG         | For `o', `x', `X', `b' and `B', radix-1
- *             | (numeric fmt) | is used for negative numbers formatted as
- *             |               | complements.
- *    ---------+---------------+-----------------------------------------
- *    *        | all           | Use the next argument as the field width. 
- *             |               | If negative, left-justify the result. If the
- *             |               | asterisk is followed by a number and a dollar 
- *             |               | sign, use the indicated argument as the width.
- *
- *  Examples of flags:
- *
- *   # `+' and space flag specifies the sign of non-negative numbers.
- *   sprintf("%d", 123)  #=> "123"
- *   sprintf("%+d", 123) #=> "+123"
- *   sprintf("% d", 123) #=> " 123"
- *
- *   # `#' flag for `o' increases number of digits to show `0'.
- *   # `+' and space flag changes format of negative numbers.
- *   sprintf("%o", 123)   #=> "173"
- *   sprintf("%#o", 123)  #=> "0173"
- *   sprintf("%+o", -123) #=> "-173"
- *   sprintf("%o", -123)  #=> "..7605"
- *   sprintf("%#o", -123) #=> "..7605"
- *
- *   # `#' flag for `x' add a prefix `0x' for non-zero numbers.
- *   # `+' and space flag disables complements for negative numbers.
- *   sprintf("%x", 123)   #=> "7b"
- *   sprintf("%#x", 123)  #=> "0x7b"
- *   sprintf("%+x", -123) #=> "-7b"
- *   sprintf("%x", -123)  #=> "..f85"
- *   sprintf("%#x", -123) #=> "0x..f85"
- *   sprintf("%#x", 0)    #=> "0"
- *
- *   # `#' for `X' uses the prefix `0X'.
- *   sprintf("%X", 123)  #=> "7B"
- *   sprintf("%#X", 123) #=> "0X7B"
- *
- *   # `#' flag for `b' add a prefix `0b' for non-zero numbers.
- *   # `+' and space flag disables complements for negative numbers.
- *   sprintf("%b", 123)   #=> "1111011"
- *   sprintf("%#b", 123)  #=> "0b1111011"
- *   sprintf("%+b", -123) #=> "-1111011"
- *   sprintf("%b", -123)  #=> "..10000101"
- *   sprintf("%#b", -123) #=> "0b..10000101"
- *   sprintf("%#b", 0)    #=> "0"
- *
- *   # `#' for `B' uses the prefix `0B'.
- *   sprintf("%B", 123)  #=> "1111011"
- *   sprintf("%#B", 123) #=> "0B1111011"
- *
- *   # `#' for `e' forces to show the decimal point.
- *   sprintf("%.0e", 1)  #=> "1e+00"
- *   sprintf("%#.0e", 1) #=> "1.e+00"
- *
- *   # `#' for `f' forces to show the decimal point.
- *   sprintf("%.0f", 1234)  #=> "1234"
- *   sprintf("%#.0f", 1234) #=> "1234."
- *
- *   # `#' for `g' forces to show the decimal point.
- *   # It also disables stripping lowest zeros.
- *   sprintf("%g", 123.4)   #=> "123.4"
- *   sprintf("%#g", 123.4)  #=> "123.400"
- *   sprintf("%g", 123456)  #=> "123456"
- *   sprintf("%#g", 123456) #=> "123456."
- *     
- *  The field width is an optional integer, followed optionally by a
- *  period and a precision.  The width specifies the minimum number of
- *  characters that will be written to the result for this field.
- *
- *  Examples of width:
- *
- *   # padding is done by spaces,       width=20
- *   # 0 or radix-1.             <------------------>
- *   sprintf("%20d", 123)   #=> "                 123"
- *   sprintf("%+20d", 123)  #=> "                +123"
- *   sprintf("%020d", 123)  #=> "00000000000000000123"
- *   sprintf("%+020d", 123) #=> "+0000000000000000123"
- *   sprintf("% 020d", 123) #=> " 0000000000000000123"
- *   sprintf("%-20d", 123)  #=> "123                 "
- *   sprintf("%-+20d", 123) #=> "+123                "
- *   sprintf("%- 20d", 123) #=> " 123                "
- *   sprintf("%020x", -123) #=> "..ffffffffffffffff85"
- *
- *  For
- *  numeric fields, the precision controls the number of decimal places
- *  displayed.  For string fields, the precision determines the maximum
- *  number of characters to be copied from the string.  (Thus, the format
- *  sequence <code>%10.10s</code> will always contribute exactly ten
- *  characters to the result.)
- *
- *  Examples of precisions:
- *
- *   # precision for `d', 'o', 'x' and 'b' is
- *   # minimum number of digits               <------>
- *   sprintf("%20.8d", 123)  #=> "            00000123"
- *   sprintf("%20.8o", 123)  #=> "            00000173"
- *   sprintf("%20.8x", 123)  #=> "            0000007b"
- *   sprintf("%20.8b", 123)  #=> "            01111011"
- *   sprintf("%20.8d", -123) #=> "           -00000123"
- *   sprintf("%20.8o", -123) #=> "            ..777605"
- *   sprintf("%20.8x", -123) #=> "            ..ffff85"
- *   sprintf("%20.8b", -11)  #=> "            ..110101"
- *
- *   # "0x" and "0b" for `#x' and `#b' is not counted for
- *   # precision but "0" for `#o' is counted.  <------>
- *   sprintf("%#20.8d", 123)  #=> "            00000123"
- *   sprintf("%#20.8o", 123)  #=> "            00000173"
- *   sprintf("%#20.8x", 123)  #=> "          0x0000007b"
- *   sprintf("%#20.8b", 123)  #=> "          0b01111011"
- *   sprintf("%#20.8d", -123) #=> "           -00000123"
- *   sprintf("%#20.8o", -123) #=> "            ..777605"
- *   sprintf("%#20.8x", -123) #=> "          0x..ffff85"
- *   sprintf("%#20.8b", -11)  #=> "          0b..110101"
- *
- *   # precision for `e' is number of
- *   # digits after the decimal point           <------>
- *   sprintf("%20.8e", 1234.56789) #=> "      1.23456789e+03"
- *                                    
- *   # precision for `f' is number of
- *   # digits after the decimal point               <------>
- *   sprintf("%20.8f", 1234.56789) #=> "       1234.56789000"
- *
- *   # precision for `g' is number of
- *   # significant digits                          <------->
- *   sprintf("%20.8g", 1234.56789) #=> "           1234.5679"
- *
- *   #                                         <------->
- *   sprintf("%20.8g", 123456789)  #=> "       1.2345679e+08"
- *
- *   # precision for `s' is
- *   # maximum number of characters                    <------>
- *   sprintf("%20.8s", "string test") #=> "            string t"
- *     
- *  Examples:
- *
- *     sprintf("%d %04x", 123, 123)               #=> "123 007b"
- *     sprintf("%08b '%4s'", 123, 123)            #=> "01111011 ' 123'"
- *     sprintf("%1$*2$s %2$d %1$s", "hello", 8)   #=> "   hello 8 hello"
- *     sprintf("%1$*2$s %2$d", "hello", -8)       #=> "hello    -8"
- *     sprintf("%+g:% g:%-g", 1.23, 1.23, 1.23)   #=> "+1.23: 1.23:1.23"
- *     sprintf("%u", -123)                        #=> "-123"
- */
-
-#define GETNTHARG(nth) \
-    ((nth >= argc) ? (rb_raise(rb_eArgError, "too few arguments"), 0) : \
-    argv[nth])
-
-extern "C" {
-
-VALUE
-rb_f_sprintf_imp(VALUE recv, SEL sel, int argc, VALUE *argv)
-{
-    return rb_str_format(argc - 1, argv + 1, GETNTHARG(0));
-}
-
-VALUE
-rb_f_sprintf(int argc, const VALUE *argv)
-{
-    return rb_str_format(argc - 1, argv + 1, GETNTHARG(0));
-}
-
-VALUE
-rb_enc_vsprintf(rb_encoding *enc, const char *fmt, va_list ap)
-{
-    char buffer[512];
-    int n;
-    n = vsnprintf(buffer, sizeof buffer, fmt, ap);
-    return rb_enc_str_new(buffer, n, enc);
-}
-
-VALUE
-rb_enc_sprintf(rb_encoding *enc, const char *format, ...)
-{
-    VALUE result;
-    va_list ap;
-
-    va_start(ap, format);
-    result = rb_enc_vsprintf(enc, format, ap);
-    va_end(ap);
-
-    return result;
-}
-
-VALUE
-rb_vsprintf(const char *fmt, va_list ap)
-{
-    return rb_enc_vsprintf(NULL, fmt, ap);
-}
-
-VALUE
-rb_sprintf(const char *format, ...)
-{
-    VALUE result;
-    va_list ap;
-
-    va_start(ap, format);
-    result = rb_vsprintf(format, ap);
-    va_end(ap);
-
-    return result;
-}
-
-#define IS_NEG(num) RBIGNUM_NEGATIVE_P(num)
-#define REL_REF	    1
-#define ABS_REF	    2
-#define NAMED_REF   3
-
-#define REF_NAME(type) \
-    ((type) == REL_REF ? "relative" : (type) == ABS_REF ? "absolute" : "named")
-
-#define SET_REF_TYPE(type) \
-    if (ref_type != 0 && (type) != ref_type) { \
-	rb_raise(rb_eArgError, "can't mix %s references with %s references", \
-		REF_NAME(type), REF_NAME(ref_type)); \
-    } \
-    ref_type = (type);
-
-#define GET_ARG() \
-    if (arg == 0) { \
-	SET_REF_TYPE(REL_REF); \
-	arg = GETNTHARG(j); \
-	j++; \
-    }
-    
-#define isprenum(ch) ((ch) == '-' || (ch) == ' ' || (ch) == '+')
-
-#define isnan(x) (x != x)
-#define isinf(x) (__builtin_fabs(x) == __builtin_inf())
-
-static void
-pad_format_value(VALUE arg, long start, long width,
-	CFStringRef pad)
-{
-    long slen = (long)CFStringGetLength((CFStringRef)arg);
-    if (width <= slen) {
-	return;
-    }
-    if (start < 0) {
-	start += slen + 1;
-    }
-    width -= slen;
-    do {
-	CFStringInsert((CFMutableStringRef)arg, start, pad);
-    } while (--width > 0);
-}
-
-static long
-cstr_update(char **str, unsigned long start, unsigned long num, char *replace)
-{
-    unsigned long len = strlen(*str) + 1;
-    unsigned long replace_len = strlen(replace);
-    if (start + num > len) {
-	num = len - start;
-    }
-    if (replace_len >= num) {
-	char *new_str = (char *)xmalloc(len + replace_len - num);
-	memcpy(new_str, *str, len);
-	*str = new_str;
-    }
-    if (replace_len != num) {
-	bcopy(*str + start + num, *str + start + replace_len, len - start -
-		num);
-    }
-    if (replace_len > 0) {
-	bcopy(replace, *str + start, replace_len);
-    }
-    return replace_len - num;
-}
-
-VALUE
-get_named_arg(char *format_str, unsigned long format_len, unsigned long *i,
-	VALUE hash)
-{
-    if (TYPE(hash) != T_HASH) {
-	rb_raise(rb_eArgError,
-		 "hash required for named references");
-    }
-    char closing = format_str[(*i)++] + 2;
-    char *str_ptr = format_str + *i;
-    while (*i < format_len && format_str[*i] != closing) {
-	(*i)++;
-    }
-    if (*i == format_len) {
-	rb_raise(rb_eArgError,
-		 "malformed name - unmatched parenthesis");
-    }
-    format_str[*i] = '\0';
-    hash = rb_hash_aref(hash, rb_name2sym(str_ptr));
-    format_str[*i] = closing;
-    return (hash);
-}
-
-// XXX
-// - this method uses strtol to read numbers from the format string, so
-//   extremely large numbers get silently truncated. this should be fixed
-// - switch to a cfstring format string to allow for proper encoding support
-    
-// XXX look for arguments that are altered but not duped
-VALUE
-rb_str_format(int argc, const VALUE *argv, VALUE fmt)
-{
-    bool tainted = OBJ_TAINTED(fmt);
-    fmt = rb_str_new3(fmt);
-    char *format_str = (char *)RSTRING_PTR(fmt);
-    unsigned long format_len = strlen(format_str);
-    long num;
-    int j = 0;
-    int ref_type = 0;
-
-    for (unsigned long i = 0; i < format_len; i++) {
-	if (format_str[i] != '%') {
-	    continue;
-	}
-	if (format_str[i + 1] == '%') {
-	    cstr_update(&format_str, i, 1, (char *)"");
-	    continue;
-	}
-
-	bool sharp_flag = false;
-	bool space_flag = false;
-	bool plus_flag = false;
-	bool minus_flag = false;
-	bool zero_flag = false;
-	bool precision_flag = false;
-	bool complete = false;
-	VALUE arg = 0;
-	long width = 0;
-	long precision = 0;
-	int base = 0;
-	CFStringRef negative_pad = NULL;
-	CFStringRef sharp_pad = CFSTR("");
-	char *str_ptr;
-
-	unsigned long start = i;
-	while (i++ < format_len) {
-	    switch (format_str[i]) {
-		case '#':
-		    sharp_flag = true;
-		    break;
-
-		case '*':
-		    if (format_str[++i] == '<' || format_str[i] == '{') {
-			SET_REF_TYPE(NAMED_REF);
-			width = NUM2LONG(rb_Integer(get_named_arg(format_str,
-				format_len, &i, GETNTHARG(0))));
-		    }
-		    else {
-			if (isprenum(format_str[i])) {
-			    i--;
-			    break;
-			}
-			num = strtol(format_str + i, &str_ptr, 10);
-			if (str_ptr == format_str + i--) {
-			    SET_REF_TYPE(REL_REF);
-			    width = NUM2LONG(rb_Integer(GETNTHARG(j)));
-			    j++;
-			}
-			else if (*str_ptr == '$') {
-			    SET_REF_TYPE(ABS_REF);
-			    width = NUM2LONG(rb_Integer(GETNTHARG(num - 1)));
-			    i = str_ptr - format_str;
-			}
-		    }
-		    if (width < 0) {
-			minus_flag = true;
-			width = -width;
-		    }
-		    break;
-
-		case ' ':
-		    if (!plus_flag) {
-			space_flag = true;
-		    }
-		    break;
-
-		case '+':
-		    plus_flag = true;
-		    space_flag = false;
-		    break;
-
-		case '-':
-		    zero_flag = false;
-		    minus_flag = true;
-		    break;
-
-		case '0':
-		    if (!precision_flag && !minus_flag) {
-			zero_flag = true;
-		    }
-		    break;
-
-		case '1':
-		case '2':
-		case '3':
-		case '4':
-		case '5':
-		case '6':
-		case '7':
-		case '8':
-		case '9':
-		    num = strtol(format_str + i, &str_ptr, 10);
-		    i = str_ptr - format_str;
-		    if (*str_ptr == '$') {
-			if (num == 0) {
-			    rb_raise(rb_eArgError, "invalid absolute argument");
-			}
-			SET_REF_TYPE(ABS_REF);
-			arg = GETNTHARG(num - 1);
-		    }
-		    else {
-			SET_REF_TYPE(REL_REF);
-			width = num;
-			i--;
-		    }
-		    break;
-
-		case '.':
-		    zero_flag = false;
-		    precision_flag = true;
-		    if (format_str[++i] == '*') {
-			if (format_str[++i] == '<' || format_str[i] == '{') {
-			    SET_REF_TYPE(NAMED_REF);
-			    precision = NUM2LONG(rb_Integer(get_named_arg(
-				    format_str, format_len, &i, GETNTHARG(0))));
-			}
-			else {
-			    if (isprenum(format_str[i])) {
-				i--;
-				break;
-			    }
-			    num = strtol(format_str + i, &str_ptr, 10);
-			    if (str_ptr == format_str + i--) {
-				SET_REF_TYPE(REL_REF);
-				precision = NUM2LONG(rb_Integer(GETNTHARG(j)));
-				j++;
-			    }
-			    else if (*str_ptr == '$') {
-				SET_REF_TYPE(ABS_REF);
-				precision = NUM2LONG(rb_Integer(GETNTHARG(
-					num - 1)));
-				i = str_ptr - format_str;
-			    }
-			}
-		    }
-		    else if (isdigit(format_str[i])) {
-			precision = strtol(format_str + i, &str_ptr, 10);
-			i = str_ptr - format_str - 1;
-		    }
-		    else {
-			rb_raise(rb_eArgError, "invalid precision");
-		    }
-
-		    if (precision < 0) {
-			precision = 0;
-		    }
-		    break;
-
-		case '<':
-		case '{':
-		    SET_REF_TYPE(NAMED_REF);
-		    arg = get_named_arg(format_str, format_len, &i,
-			    GETNTHARG(0));
-		    break;
-
-		case 'd':
-		case 'D':
-		case 'i':
-		case 'u':
-		case 'U':
-		    base = 10;
-		    complete = true;
-		    break;
-
-		case 'x':
-		case 'X':
-		    base = 16;
-		    negative_pad = CFSTR("f");
-		    sharp_pad = CFSTR("0x");
-		    complete = true;
-		    break;
-
-		case 'o':
-		case 'O':
-		    base = 8;
-		    negative_pad = CFSTR("7");
-		    sharp_pad = CFSTR("0");
-		    complete = true;
-		    break;
-
-		case 'B':
-		case 'b':
-		    base = 2;
-		    negative_pad = CFSTR("1");
-		    sharp_pad = CFSTR("0b");
-		    complete = true;
-		    break;
-
-		case 'c':
-		case 'C':
-		    GET_ARG();
-		    if (TYPE(arg) == T_STRING) {
-			arg = rb_str_substr(arg, 0, 1);
-		    }
-		    else {
-			// rb_num_to_chr is broken so leave out the
-			// enc or we don't get range checking
-			arg = rb_num_to_chr(arg, NULL /*rb_enc_get(fmt)*/);
-		    }
-		    complete = true;
-		    break;
-
-		case 'f':
-		case 'F':
-		case 'e':
-		case 'E':
-		case 'g':
-		case 'G':
-		case 'a':
-		case 'A':
-		{
-		    // here we construct a new format str and then use
-		    // c's sprintf. why? because floats are retarded
-		    GET_ARG();
-		    double value = RFLOAT_VALUE(rb_Float(arg));
-		    complete = true;
-		    
-		    if (isnan(value) || isinf(value)) {
-			arg = rb_str_new2((char *)(isnan(value) ? "NaN" :
-				value < 0 ? "-Inf" : "Inf"));
-			if (isnan(value) || value > 0) {
-			    if (plus_flag) {
-				rb_str_update(arg, 0, 0, (VALUE)CFSTR("+"));
-			    }
-			    else if (space_flag) {
-				rb_str_update(arg, 0, 0, (VALUE)CFSTR(" "));
-			    }
-			}
-			break;
-		    }
-
-		    arg = rb_str_new(format_str + i, 1);
-		    if (precision_flag) {
-			rb_str_update(arg, 0, 0, rb_big2str(LONG2NUM(precision),
-				10));
-			rb_str_update(arg, 0, 0, (VALUE)CFSTR("."));
-		    }
-		    rb_str_update(arg, 0, 0, rb_big2str(LONG2NUM(width), 10));
-		    if (minus_flag) {
-			rb_str_update(arg, 0, 0, (VALUE)CFSTR("-"));
-		    }
-		    else if (zero_flag) {
-			rb_str_update(arg, 0, 0, (VALUE)CFSTR("0"));
-		    }
-		    if (plus_flag) {
-			rb_str_update(arg, 0, 0, (VALUE)CFSTR("+"));
-		    }
-		    else if (space_flag) {
-			rb_str_update(arg, 0, 0, (VALUE)CFSTR(" "));
-		    }
-		    if (sharp_flag) {
-			rb_str_update(arg, 0, 0, (VALUE)CFSTR("#"));
-		    }
-		    rb_str_update(arg, 0, 0, (VALUE)CFSTR("%"));
-
-		    asprintf(&str_ptr, RSTRING_PTR(arg), value);
-		    arg = rb_str_new2(str_ptr);
-		    free(str_ptr);
-		    break;
-		}
-
-		case 's':
-		case 'S':
-		case 'p':
-		case '@':
-		    GET_ARG();
-		    arg = (tolower(format_str[i]) != 's' ? rb_inspect(arg)
-			    : TYPE(arg) == T_STRING ? rb_str_new3(arg)
-			    : rb_obj_as_string(arg));
-		    if (precision_flag && precision
-			    < CFStringGetLength((CFStringRef)arg)) {
-			CFStringPad((CFMutableStringRef)arg, NULL, precision,
-				0);
-		    }
-		    complete = true;
-		    break;
-
-		default:
-		    rb_raise(rb_eArgError, "malformed format string - %%%c",
-			    format_str[i]);
-	    }
-	    if (!complete) {
-		continue;
-	    }
-
-	    GET_ARG();
-
-	    if (base != 0) {
-		bool sign_pad = false;
-		unsigned long num_index = 0;
-		CFStringRef zero_pad = CFSTR("0");
-
-		VALUE num = rb_Integer(arg);
-		if (TYPE(num) == T_FIXNUM) {
-		    num = rb_int2big(FIX2LONG(num));
-		}
-		if (plus_flag || space_flag) {
-		    sign_pad = 1;
-		}
-		if (IS_NEG(num)) {
-		    num_index = 1;
-		    if (!sign_pad && negative_pad != NULL) {
-			zero_pad = negative_pad;
-			num = rb_big_clone(num);
-			rb_big_2comp(num);
-		    }
-		}
-
-		arg = rb_big2str(num, base);
-		if (!sign_pad && IS_NEG(num) && negative_pad != NULL) {
-		    char neg = *RSTRING_PTR(negative_pad);
-		    str_ptr = (char *)RSTRING_PTR(arg) + 1;
-		    if (base == 8) {
-			*str_ptr |= ((~0 << 3) >> ((3 * strlen(str_ptr)) %
-				(sizeof(BDIGIT) * 8))) & ~(~0 << 3);
-		    }
-		    while (*str_ptr++ == neg) {
-			num_index++;
-		    }
-		    rb_str_update(arg, 0, num_index, (VALUE)negative_pad);
-		    rb_str_update(arg, 0, 0, (VALUE)CFSTR(".."));
-		    num_index = 2;
-		}
-
-		if (precision_flag) {
-		    pad_format_value(arg, num_index, precision + (IS_NEG(num) &&
-			    (sign_pad || negative_pad == NULL) ? 1 : 0),
-			    zero_pad);
-		}
-		if (sharp_flag && rb_cmpint(num, NULL, NULL) != 0) {
-		    rb_str_update(arg, sign_pad, 0, (VALUE)sharp_pad);
-		    num_index += 2;
-		}
-		if (sign_pad && RBIGNUM_POSITIVE_P(num)) {
-		    rb_str_update(arg, 0, 0, (VALUE)(plus_flag ?
-			    CFSTR("+") : CFSTR(" ")));
-		    num_index++;
-		}
-		if (zero_flag) {
-		    pad_format_value(arg, num_index, width, zero_pad);
-		}
-		if (ISUPPER(format_str[i])) {
-		    CFStringUppercase((CFMutableStringRef)arg, NULL);
-		}
-	    }
-	    
-	    if (OBJ_TAINTED(arg)) {
-		tainted = true;
-	    }
-
-	    pad_format_value(arg, minus_flag ? -1 : 0, width, CFSTR(" "));
-	    num = cstr_update(&format_str, start, i - start + 1,
-		    (char *)RSTRING_PTR(arg));
-	    format_len += num;
-	    i += num;
-	    break;
-	}
-    }
-
-    fmt = rb_str_new2(format_str);
-    return tainted ? OBJ_TAINT(fmt) : fmt;
-}
-
-} // extern "C"
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/macruby-changes/attachments/20100210/5225e377/attachment-0001.html>


More information about the macruby-changes mailing list