[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