platform/shared/ruby/sprintf.c in rhodes-5.5.18 vs platform/shared/ruby/sprintf.c in rhodes-6.0.11
- old
+ new
@@ -1,61 +1,32 @@
/**********************************************************************
sprintf.c -
- $Author: yugui $
+ $Author$
created at: Fri Oct 15 10:39:26 JST 1993
Copyright (C) 1993-2007 Yukihiro Matsumoto
Copyright (C) 2000 Network Applied Communication Laboratory, Inc.
Copyright (C) 2000 Information-technology Promotion Agency, Japan
**********************************************************************/
-#include "ruby/ruby.h"
+#include "internal.h"
#include "ruby/re.h"
-#include "ruby/encoding.h"
+#include "id.h"
#include <math.h>
#include <stdarg.h>
#ifdef HAVE_IEEEFP_H
#include <ieeefp.h>
#endif
#define BIT_DIGITS(N) (((N)*146)/485 + 1) /* log2(10) =~ 146/485 */
-#define BITSPERDIG (SIZEOF_BDIGITS*CHAR_BIT)
-#define EXTENDSIGN(n, l) (((~0 << (n)) >> (((n)*(l)) % BITSPERDIG)) & ~(~0 << (n)))
static void fmt_setup(char*,size_t,int,int,int,int);
-static char*
-remove_sign_bits(char *str, int base)
-{
- char *s, *t;
-
- s = t = str;
-
- if (base == 16) {
- while (*t == 'f') {
- t++;
- }
- }
- else if (base == 8) {
- *t |= EXTENDSIGN(3, strlen(t));
- while (*t == '7') {
- t++;
- }
- }
- else if (base == 2) {
- while (*t == '1') {
- t++;
- }
- }
-
- return t;
-}
-
static char
sign_bits(int base, const char *p)
{
char c = '.';
@@ -92,80 +63,121 @@
buf = RSTRING_PTR(result);\
} while (0)
#define PUSH(s, l) do { \
CHECK(l);\
- memcpy(&buf[blen], s, l);\
+ memcpy(&buf[blen], (s), (l));\
blen += (l);\
} while (0)
#define FILL(c, l) do { \
CHECK(l);\
- memset(&buf[blen], c, l);\
+ memset(&buf[blen], (c), (l));\
blen += (l);\
} while (0)
#define GETARG() (nextvalue != Qundef ? nextvalue : \
- posarg == -1 ? \
- (rb_raise(rb_eArgError, "unnumbered(%d) mixed with numbered", nextarg), 0) : \
- posarg == -2 ? \
- (rb_raise(rb_eArgError, "unnumbered(%d) mixed with named", nextarg), 0) : \
+ GETNEXTARG())
+
+#define GETNEXTARG() ( \
+ check_next_arg(posarg, nextarg), \
(posarg = nextarg++, GETNTHARG(posarg)))
-#define GETPOSARG(n) (posarg > 0 ? \
- (rb_raise(rb_eArgError, "numbered(%d) after unnumbered(%d)", n, posarg), 0) : \
- posarg == -2 ? \
- (rb_raise(rb_eArgError, "numbered(%d) after named", n), 0) : \
- ((n < 1) ? (rb_raise(rb_eArgError, "invalid index - %d$", n), 0) : \
- (posarg = -1, GETNTHARG(n))))
+#define GETPOSARG(n) ( \
+ check_pos_arg(posarg, (n)), \
+ (posarg = -1, GETNTHARG(n)))
#define GETNTHARG(nth) \
- ((nth >= argc) ? (rb_raise(rb_eArgError, "too few arguments"), 0) : argv[nth])
+ (((nth) >= argc) ? (rb_raise(rb_eArgError, "too few arguments"), 0) : argv[(nth)])
-#define GETNAMEARG(id, name, len) ( \
- posarg > 0 ? \
- (rb_raise(rb_eArgError, "named%.*s after unnumbered(%d)", (len), (name), posarg), 0) : \
- posarg == -1 ? \
- (rb_raise(rb_eArgError, "named%.*s after numbered", (len), (name)), 0) : \
- (posarg = -2, rb_hash_lookup2(get_hash(&hash, argc, argv), id, Qundef)))
+#define CHECKNAMEARG(name, len, enc) ( \
+ check_name_arg(posarg, name, len, enc), \
+ posarg = -2)
#define GETNUM(n, val) \
- for (; p < end && rb_enc_isdigit(*p, enc); p++) { \
- int next_n = 10 * n + (*p - '0'); \
- if (next_n / 10 != n) {\
- rb_raise(rb_eArgError, #val " too big"); \
- } \
- n = next_n; \
- } \
- if (p >= end) { \
- rb_raise(rb_eArgError, "malformed format string - %%*[0-9]"); \
- }
+ (!(p = get_num(p, end, enc, &(n))) ? \
+ rb_raise(rb_eArgError, #val " too big") : (void)0)
#define GETASTER(val) do { \
t = p++; \
n = 0; \
GETNUM(n, val); \
if (*p == '$') { \
tmp = GETPOSARG(n); \
} \
else { \
- tmp = GETARG(); \
+ tmp = GETNEXTARG(); \
p = t; \
} \
- val = NUM2INT(tmp); \
+ (val) = NUM2INT(tmp); \
} while (0)
+static const char *
+get_num(const char *p, const char *end, rb_encoding *enc, int *valp)
+{
+ int next_n = *valp;
+ for (; p < end && rb_enc_isdigit(*p, enc); p++) {
+ if (MUL_OVERFLOW_INT_P(10, next_n))
+ return NULL;
+ next_n *= 10;
+ if (INT_MAX - (*p - '0') < next_n)
+ return NULL;
+ next_n += *p - '0';
+ }
+ if (p >= end) {
+ rb_raise(rb_eArgError, "malformed format string - %%*[0-9]");
+ }
+ *valp = next_n;
+ return p;
+}
+
+static void
+check_next_arg(int posarg, int nextarg)
+{
+ switch (posarg) {
+ case -1:
+ rb_raise(rb_eArgError, "unnumbered(%d) mixed with numbered", nextarg);
+ case -2:
+ rb_raise(rb_eArgError, "unnumbered(%d) mixed with named", nextarg);
+ }
+}
+
+static void
+check_pos_arg(int posarg, int n)
+{
+ if (posarg > 0) {
+ rb_raise(rb_eArgError, "numbered(%d) after unnumbered(%d)", n, posarg);
+ }
+ if (posarg == -2) {
+ rb_raise(rb_eArgError, "numbered(%d) after named", n);
+ }
+ if (n < 1) {
+ rb_raise(rb_eArgError, "invalid index - %d$", n);
+ }
+}
+
+static void
+check_name_arg(int posarg, const char *name, int len, rb_encoding *enc)
+{
+ if (posarg > 0) {
+ rb_enc_raise(enc, rb_eArgError, "named%.*s after unnumbered(%d)", len, name, posarg);
+ }
+ if (posarg == -1) {
+ rb_enc_raise(enc, rb_eArgError, "named%.*s after numbered", len, name);
+ }
+}
+
static VALUE
get_hash(volatile VALUE *hash, int argc, const VALUE *argv)
{
VALUE tmp;
if (*hash != Qundef) return *hash;
if (argc != 2) {
rb_raise(rb_eArgError, "one hash required");
}
- tmp = rb_check_convert_type(argv[1], T_HASH, "Hash", "to_hash");
+ tmp = rb_check_hash_type(argv[1]);
if (NIL_P(tmp)) {
rb_raise(rb_eArgError, "one hash required");
}
return (*hash = tmp);
}
@@ -422,11 +434,11 @@
* sprintf("%u", -123) #=> "-123"
*
* For more complex formatting, Ruby supports a reference by name.
* %<name>s style uses format style, but %{name} style doesn't.
*
- * Exapmles:
+ * Examples:
* sprintf("%<foo>d : %<bar>f", { :foo => 1, :bar => 2 })
* #=> 1 : 2.000000
* sprintf("%{foo}f", { :foo => 1 })
* # => "1f"
*/
@@ -438,10 +450,11 @@
}
VALUE
rb_str_format(int argc, const VALUE *argv, VALUE fmt)
{
+ enum {default_float_precision = 6};
rb_encoding *enc;
const char *p, *end;
char *buf;
long blen, bsiz;
VALUE result;
@@ -489,11 +502,11 @@
ENC_CODERANGE_SET(result, coderange);
for (; p < end; p++) {
const char *t;
int n;
- ID id = 0;
+ VALUE sym = Qnil;
for (t = p; t < end && *t != '%'; t++) ;
PUSH(p, t - p);
if (coderange != ENC_CODERANGE_BROKEN && scanned < blen) {
scanned += rb_str_coderange_scan_restartable(buf+scanned, buf+blen, enc, &coderange);
@@ -566,25 +579,48 @@
case '<':
case '{':
{
const char *start = p;
char term = (*p == '<') ? '>' : '}';
+ int len;
for (; p < end && *p != term; ) {
p += rb_enc_mbclen(p, end, enc);
}
if (p >= end) {
rb_raise(rb_eArgError, "malformed name - unmatched parenthesis");
}
- if (id) {
- rb_raise(rb_eArgError, "name%.*s after <%s>",
- (int)(p - start + 1), start, rb_id2name(id));
+#if SIZEOF_INT < SIZEOF_SIZE_T
+ if ((size_t)(p - start) >= INT_MAX) {
+ const int message_limit = 20;
+ len = (int)(rb_enc_right_char_head(start, start + message_limit, p, enc) - start);
+ rb_enc_raise(enc, rb_eArgError,
+ "too long name (%"PRIdSIZE" bytes) - %.*s...%c",
+ (size_t)(p - start - 2), len, start, term);
}
- id = rb_intern3(start + 1, p - start - 1, enc);
- nextvalue = GETNAMEARG(ID2SYM(id), start, (int)(p - start + 1));
+#endif
+ len = (int)(p - start + 1); /* including parenthesis */
+ if (sym != Qnil) {
+ rb_enc_raise(enc, rb_eArgError, "named%.*s after <%"PRIsVALUE">",
+ len, start, rb_sym2str(sym));
+ }
+ CHECKNAMEARG(start, len, enc);
+ get_hash(&hash, argc, argv);
+ sym = rb_check_symbol_cstr(start + 1,
+ len - 2 /* without parenthesis */,
+ enc);
+ if (!NIL_P(sym)) nextvalue = rb_hash_lookup2(hash, sym, Qundef);
if (nextvalue == Qundef) {
- rb_raise(rb_eKeyError, "key%.*s not found", (int)(p - start + 1), start);
+ if (NIL_P(sym)) {
+ sym = rb_sym_intern(start + 1,
+ len - 2 /* without parenthesis */,
+ enc);
+ }
+ nextvalue = rb_hash_default_value(hash, sym);
+ if (NIL_P(nextvalue)) {
+ rb_enc_raise(enc, rb_eKeyError, "key%.*s not found", len, start);
+ }
}
if (term == '}') goto format_s;
p++;
goto retry;
}
@@ -641,10 +677,11 @@
if (!NIL_P(tmp)) {
if (rb_enc_strlen(RSTRING_PTR(tmp),RSTRING_END(tmp),enc) != 1) {
rb_raise(rb_eArgError, "%%c requires a character");
}
c = rb_enc_codepoint_len(RSTRING_PTR(tmp), RSTRING_END(tmp), &n, enc);
+ RB_GC_GUARD(tmp);
}
else {
c = NUM2INT(val);
n = rb_enc_codelen(c, enc);
}
@@ -658,14 +695,14 @@
}
else if ((flags & FMINUS)) {
CHECK(n);
rb_enc_mbcput(c, &buf[blen], enc);
blen += n;
- FILL(' ', width-1);
+ if (width > 1) FILL(' ', width-1);
}
else {
- FILL(' ', width-1);
+ if (width > 1) FILL(' ', width-1);
CHECK(n);
rb_enc_mbcput(c, &buf[blen], enc);
blen += n;
}
}
@@ -676,12 +713,16 @@
format_s:
{
VALUE arg = GETARG();
long len, slen;
- if (*p == 'p') arg = rb_inspect(arg);
- str = rb_obj_as_string(arg);
+ if (*p == 'p') {
+ str = rb_inspect(arg);
+ }
+ else {
+ str = rb_obj_as_string(arg);
+ }
if (OBJ_TAINTED(str)) tainted = 1;
len = RSTRING_LEN(str);
rb_str_set_len(result, blen);
if (coderange != ENC_CODERANGE_BROKEN && scanned < blen) {
int cr = coderange;
@@ -711,10 +752,11 @@
buf[blen++] = ' ';
}
}
CHECK(len);
memcpy(&buf[blen], RSTRING_PTR(str), len);
+ RB_GC_GUARD(str);
blen += len;
if (flags&FMINUS) {
CHECK(width);
while (width--) {
buf[blen++] = ' ';
@@ -723,10 +765,11 @@
rb_enc_associate(result, enc);
break;
}
}
PUSH(RSTRING_PTR(str), len);
+ RB_GC_GUARD(str);
rb_enc_associate(result, enc);
}
break;
case 'd':
@@ -736,19 +779,19 @@
case 'X':
case 'b':
case 'B':
case 'u':
{
- volatile VALUE tmp1;
volatile VALUE val = GETARG();
- char fbuf[32], nbuf[64], *s;
+ int valsign;
+ char nbuf[64], *s;
const char *prefix = 0;
int sign = 0, dots = 0;
char sc = 0;
long v = 0;
int base, bignum = 0;
- int len, pos;
+ int len;
switch (*p) {
case 'd':
case 'i':
case 'u':
@@ -815,104 +858,105 @@
case 'i':
default:
base = 10; break;
}
- if (!bignum) {
- if (base == 2) {
- val = rb_int2big(v);
- goto bin_retry;
- }
- if (sign) {
- char c = *p;
- if (c == 'i') c = 'd'; /* %d and %i are identical */
- if (v < 0) {
- v = -v;
- sc = '-';
- width--;
- }
- else if (flags & FPLUS) {
- sc = '+';
- width--;
- }
- else if (flags & FSPACE) {
- sc = ' ';
- width--;
- }
- snprintf(fbuf, sizeof(fbuf), "%%l%c", c);
- snprintf(nbuf, sizeof(nbuf), fbuf, v);
- s = nbuf;
- }
- else {
- s = nbuf;
- if (v < 0) {
- dots = 1;
- }
- snprintf(fbuf, sizeof(fbuf), "%%l%c", *p == 'X' ? 'x' : *p);
- snprintf(++s, sizeof(nbuf) - 1, fbuf, v);
- if (v < 0) {
- char d = 0;
-
- s = remove_sign_bits(s, base);
- switch (base) {
- case 16:
- d = 'f'; break;
- case 8:
- d = '7'; break;
- }
- if (d && *s != d) {
- *--s = d;
- }
- }
- }
+ if (base != 10) {
+ int numbits = ffs(base)-1;
+ size_t abs_nlz_bits;
+ size_t numdigits = rb_absint_numwords(val, numbits, &abs_nlz_bits);
+ long i;
+ if (INT_MAX-1 < numdigits) /* INT_MAX is used because rb_long2int is used later. */
+ rb_raise(rb_eArgError, "size too big");
+ if (sign) {
+ if (numdigits == 0)
+ numdigits = 1;
+ tmp = rb_str_new(NULL, numdigits);
+ valsign = rb_integer_pack(val, RSTRING_PTR(tmp), RSTRING_LEN(tmp),
+ 1, CHAR_BIT-numbits, INTEGER_PACK_BIG_ENDIAN);
+ for (i = 0; i < RSTRING_LEN(tmp); i++)
+ RSTRING_PTR(tmp)[i] = ruby_digitmap[((unsigned char *)RSTRING_PTR(tmp))[i]];
+ s = RSTRING_PTR(tmp);
+ if (valsign < 0) {
+ sc = '-';
+ width--;
+ }
+ else if (flags & FPLUS) {
+ sc = '+';
+ width--;
+ }
+ else if (flags & FSPACE) {
+ sc = ' ';
+ width--;
+ }
+ }
+ else {
+ /* Following conditional "numdigits++" guarantees the
+ * most significant digit as
+ * - '1'(bin), '7'(oct) or 'f'(hex) for negative numbers
+ * - '0' for zero
+ * - not '0' for positive numbers.
+ *
+ * It also guarantees the most significant two
+ * digits will not be '11'(bin), '77'(oct), 'ff'(hex)
+ * or '00'. */
+ if (numdigits == 0 ||
+ ((abs_nlz_bits != (size_t)(numbits-1) ||
+ !rb_absint_singlebit_p(val)) &&
+ (!bignum ? v < 0 : BIGNUM_NEGATIVE_P(val))))
+ numdigits++;
+ tmp = rb_str_new(NULL, numdigits);
+ valsign = rb_integer_pack(val, RSTRING_PTR(tmp), RSTRING_LEN(tmp),
+ 1, CHAR_BIT-numbits, INTEGER_PACK_2COMP | INTEGER_PACK_BIG_ENDIAN);
+ for (i = 0; i < RSTRING_LEN(tmp); i++)
+ RSTRING_PTR(tmp)[i] = ruby_digitmap[((unsigned char *)RSTRING_PTR(tmp))[i]];
+ s = RSTRING_PTR(tmp);
+ dots = valsign < 0;
+ }
+ len = rb_long2int(RSTRING_END(tmp) - s);
+ }
+ else if (!bignum) {
+ valsign = 1;
+ if (v < 0) {
+ v = -v;
+ sc = '-';
+ width--;
+ valsign = -1;
+ }
+ else if (flags & FPLUS) {
+ sc = '+';
+ width--;
+ }
+ else if (flags & FSPACE) {
+ sc = ' ';
+ width--;
+ }
+ snprintf(nbuf, sizeof(nbuf), "%ld", v);
+ s = nbuf;
len = (int)strlen(s);
}
else {
- if (sign) {
- tmp = rb_big2str(val, base);
- s = RSTRING_PTR(tmp);
- if (s[0] == '-') {
- s++;
- sc = '-';
- width--;
- }
- else if (flags & FPLUS) {
- sc = '+';
- width--;
- }
- else if (flags & FSPACE) {
- sc = ' ';
- width--;
- }
- }
- else {
- if (!RBIGNUM_SIGN(val)) {
- val = rb_big_clone(val);
- rb_big_2comp(val);
- }
- tmp1 = tmp = rb_big2str0(val, base, RBIGNUM_SIGN(val));
- s = RSTRING_PTR(tmp);
- if (*s == '-') {
- dots = 1;
- if (base == 10) {
- rb_warning("negative number for %%u specifier");
- }
- s = remove_sign_bits(++s, base);
- switch (base) {
- case 16:
- if (s[0] != 'f') *--s = 'f'; break;
- case 8:
- if (s[0] != '7') *--s = '7'; break;
- case 2:
- if (s[0] != '1') *--s = '1'; break;
- }
- }
- }
+ tmp = rb_big2str(val, 10);
+ s = RSTRING_PTR(tmp);
+ valsign = 1;
+ if (s[0] == '-') {
+ s++;
+ sc = '-';
+ width--;
+ valsign = -1;
+ }
+ else if (flags & FPLUS) {
+ sc = '+';
+ width--;
+ }
+ else if (flags & FSPACE) {
+ sc = ' ';
+ width--;
+ }
len = rb_long2int(RSTRING_END(tmp) - s);
}
- pos = -1;
if (dots) {
prec -= 2;
width -= 2;
}
@@ -964,46 +1008,142 @@
int plen = (int)strlen(prefix);
PUSH(prefix, plen);
}
CHECK(prec - len);
if (dots) PUSH("..", 2);
- if (!bignum && v < 0) {
+ if (!sign && valsign < 0) {
char c = sign_bits(base, p);
while (len < prec--) {
buf[blen++] = c;
}
}
else if ((flags & (FMINUS|FPREC)) != FMINUS) {
- char c;
-
- if (!sign && bignum && !RBIGNUM_SIGN(val))
- c = sign_bits(base, p);
- else
- c = '0';
while (len < prec--) {
- buf[blen++] = c;
+ buf[blen++] = '0';
}
}
PUSH(s, len);
+ RB_GC_GUARD(tmp);
CHECK(width);
while (width-- > 0) {
buf[blen++] = ' ';
}
}
break;
case 'f':
+ {
+ VALUE val = GETARG(), num, den;
+ int sign = (flags&FPLUS) ? 1 : 0, zero = 0;
+ long len, done = 0;
+ int prefix = 0;
+ if (FIXNUM_P(val) || RB_TYPE_P(val, T_BIGNUM)) {
+ den = INT2FIX(1);
+ num = val;
+ }
+ else if (RB_TYPE_P(val, T_RATIONAL)) {
+ den = rb_rational_den(val);
+ num = rb_rational_num(val);
+ }
+ else {
+ nextvalue = val;
+ goto float_value;
+ }
+ if (!(flags&FPREC)) prec = default_float_precision;
+ if (FIXNUM_P(num)) {
+ if ((SIGNED_VALUE)num < 0) {
+ long n = -FIX2LONG(num);
+ num = LONG2FIX(n);
+ sign = -1;
+ }
+ }
+ else if (rb_num_negative_p(num)) {
+ sign = -1;
+ num = rb_funcallv(num, idUMinus, 0, 0);
+ }
+ if (den != INT2FIX(1) || prec > 1) {
+ const ID idDiv = rb_intern("div");
+ VALUE p10 = rb_int_positive_pow(10, prec);
+ VALUE den_2 = rb_funcall(den, idDiv, 1, INT2FIX(2));
+ num = rb_funcallv(num, '*', 1, &p10);
+ num = rb_funcallv(num, '+', 1, &den_2);
+ num = rb_funcallv(num, idDiv, 1, &den);
+ }
+ else if (prec >= 0) {
+ zero = prec;
+ }
+ val = rb_obj_as_string(num);
+ len = RSTRING_LEN(val) + zero;
+ if (prec >= len) len = prec + 1; /* integer part 0 */
+ if (sign || (flags&FSPACE)) ++len;
+ if (prec > 0) ++len; /* period */
+ CHECK(len > width ? len : width);
+ if (sign || (flags&FSPACE)) {
+ buf[blen++] = sign > 0 ? '+' : sign < 0 ? '-' : ' ';
+ prefix++;
+ done++;
+ }
+ len = RSTRING_LEN(val) + zero;
+ t = RSTRING_PTR(val);
+ if (len > prec) {
+ memcpy(&buf[blen], t, len - prec);
+ blen += len - prec;
+ done += len - prec;
+ }
+ else {
+ buf[blen++] = '0';
+ done++;
+ }
+ if (prec > 0) {
+ buf[blen++] = '.';
+ done++;
+ }
+ if (zero) {
+ FILL('0', zero);
+ done += zero;
+ }
+ else if (prec > len) {
+ FILL('0', prec - len);
+ memcpy(&buf[blen], t, len);
+ blen += len;
+ done += prec;
+ }
+ else if (prec > 0) {
+ memcpy(&buf[blen], t + len - prec, prec);
+ blen += prec;
+ done += prec;
+ }
+ if ((flags & FWIDTH) && width > done) {
+ int fill = ' ';
+ long shifting = 0;
+ if (!(flags&FMINUS)) {
+ shifting = done;
+ if (flags&FZERO) {
+ shifting -= prefix;
+ fill = '0';
+ }
+ blen -= shifting;
+ memmove(&buf[blen + width - done], &buf[blen], shifting);
+ }
+ FILL(fill, width - done);
+ blen += shifting;
+ }
+ RB_GC_GUARD(val);
+ break;
+ }
case 'g':
case 'G':
case 'e':
case 'E':
+ /* TODO: rational support */
case 'a':
case 'A':
+ float_value:
{
VALUE val = GETARG();
double fval;
- int i, need = 6;
+ int i, need;
char fbuf[32];
fval = RFLOAT_VALUE(rb_Float(val));
if (isnan(fval) || isinf(fval)) {
const char *expr;
@@ -1051,11 +1191,11 @@
i = INT_MIN;
frexp(fval, &i);
if (i > 0)
need = BIT_DIGITS(i);
}
- need += (flags&FPREC) ? prec : 6;
+ need += (flags&FPREC) ? prec : default_float_precision;
if ((flags&FWIDTH) && need < width)
need = width;
need += 20;
CHECK(need);
@@ -1066,10 +1206,11 @@
}
flags = FNONE;
}
sprint_exit:
+ RB_GC_GUARD(fmt);
/* XXX - We cannot validate the number of arguments if (digit)$ style used.
*/
if (posarg >= 0 && nextarg < argc) {
const char *mesg = "too many arguments for format string";
if (RTEST(ruby_debug)) rb_raise(rb_eArgError, "%s", mesg);
@@ -1119,15 +1260,63 @@
# define _HAVE_SANE_QUAD_
# define _HAVE_LLP64_
# define quad_t LONG_LONG
# define u_quad_t unsigned LONG_LONG
# endif
+#elif SIZEOF_LONG != SIZEOF_LONG_LONG && SIZEOF_LONG_LONG == 8
+# define _HAVE_SANE_QUAD_
+# define quad_t LONG_LONG
+# define u_quad_t unsigned LONG_LONG
#endif
#define FLOATING_POINT 1
#define BSD__dtoa ruby_dtoa
+#define BSD__hdtoa ruby_hdtoa
+#ifdef RUBY_PRI_VALUE_MARK
+# define PRI_EXTRA_MARK RUBY_PRI_VALUE_MARK
+#endif
+#define lower_hexdigits (ruby_hexdigits+0)
+#define upper_hexdigits (ruby_hexdigits+16)
#include "vsnprintf.c"
+int
+ruby_vsnprintf(char *str, size_t n, const char *fmt, va_list ap)
+{
+ int ret;
+ rb_printf_buffer f;
+
+ if ((int)n < 1)
+ return (EOF);
+ f._flags = __SWR | __SSTR;
+ f._bf._base = f._p = (unsigned char *)str;
+ f._bf._size = f._w = n - 1;
+ f.vwrite = BSD__sfvwrite;
+ f.vextra = 0;
+ ret = (int)BSD_vfprintf(&f, fmt, ap);
+ *f._p = 0;
+ return ret;
+}
+
+int
+ruby_snprintf(char *str, size_t n, char const *fmt, ...)
+{
+ int ret;
+ va_list ap;
+
+ if ((int)n < 1)
+ return (EOF);
+
+ va_start(ap, fmt);
+ ret = ruby_vsnprintf(str, n, fmt, ap);
+ va_end(ap);
+ return ret;
+}
+
+typedef struct {
+ rb_printf_buffer base;
+ volatile VALUE value;
+} rb_printf_buffer_extra;
+
static int
ruby__sfvwrite(register rb_printf_buffer *fp, register struct __suio *uio)
{
struct __siov *iov;
VALUE result = (VALUE)fp->_bf._base;
@@ -1147,31 +1336,100 @@
MEMCPY(buf, iov->iov_base, char, n = iov->iov_len);
buf += n;
len -= n;
}
fp->_p = (unsigned char *)buf;
+ rb_str_set_len(result, buf - RSTRING_PTR(result));
return 0;
}
+static const char *
+ruby__sfvextra(rb_printf_buffer *fp, size_t valsize, void *valp, long *sz, int sign)
+{
+ VALUE value, result = (VALUE)fp->_bf._base;
+ rb_encoding *enc;
+ char *cp;
+
+ if (valsize != sizeof(VALUE)) return 0;
+ value = *(VALUE *)valp;
+ if (RBASIC(result)->klass) {
+ rb_raise(rb_eRuntimeError, "rb_vsprintf reentered");
+ }
+ if (sign == '+') {
+ if (RB_TYPE_P(value, T_CLASS)) {
+# define LITERAL(str) (*sz = rb_strlen_lit(str), str)
+
+ if (value == rb_cNilClass) {
+ return LITERAL("nil");
+ }
+ else if (value == rb_cFixnum) {
+ return LITERAL("Fixnum");
+ }
+ else if (value == rb_cSymbol) {
+ return LITERAL("Symbol");
+ }
+ else if (value == rb_cTrueClass) {
+ return LITERAL("true");
+ }
+ else if (value == rb_cFalseClass) {
+ return LITERAL("false");
+ }
+# undef LITERAL
+ }
+ value = rb_inspect(value);
+ }
+ else {
+ value = rb_obj_as_string(value);
+ if (sign == ' ') value = QUOTE(value);
+ }
+ enc = rb_enc_compatible(result, value);
+ if (enc) {
+ rb_enc_associate(result, enc);
+ }
+ else {
+ enc = rb_enc_get(result);
+ value = rb_str_conv_enc_opts(value, rb_enc_get(value), enc,
+ ECONV_UNDEF_REPLACE|ECONV_INVALID_REPLACE,
+ Qnil);
+ *(volatile VALUE *)valp = value;
+ }
+ StringValueCStr(value);
+ RSTRING_GETMEM(value, cp, *sz);
+ ((rb_printf_buffer_extra *)fp)->value = value;
+ OBJ_INFECT(result, value);
+ return cp;
+}
+
VALUE
rb_enc_vsprintf(rb_encoding *enc, const char *fmt, va_list ap)
{
- rb_printf_buffer f;
+ rb_printf_buffer_extra buffer;
+#define f buffer.base
VALUE result;
f._flags = __SWR | __SSTR;
f._bf._size = 0;
f._w = 120;
result = rb_str_buf_new(f._w);
- if (enc) rb_enc_associate(result, enc);
+ if (enc) {
+ if (rb_enc_mbminlen(enc) > 1) {
+ /* the implementation deeply depends on plain char */
+ rb_raise(rb_eArgError, "cannot construct wchar_t based encoding string: %s",
+ rb_enc_name(enc));
+ }
+ rb_enc_associate(result, enc);
+ }
f._bf._base = (unsigned char *)result;
f._p = (unsigned char *)RSTRING_PTR(result);
- RBASIC(result)->klass = 0;
+ RBASIC_CLEAR_CLASS(result);
f.vwrite = ruby__sfvwrite;
+ f.vextra = ruby__sfvextra;
+ buffer.value = 0;
BSD_vfprintf(&f, fmt, ap);
- RBASIC(result)->klass = rb_cString;
+ RBASIC_SET_CLASS_RAW(result, rb_cString);
rb_str_resize(result, (char *)f._p - RSTRING_PTR(result));
+#undef f
return result;
}
VALUE
@@ -1207,25 +1465,29 @@
}
VALUE
rb_str_vcatf(VALUE str, const char *fmt, va_list ap)
{
- rb_printf_buffer f;
+ rb_printf_buffer_extra buffer;
+#define f buffer.base
VALUE klass;
StringValue(str);
rb_str_modify(str);
f._flags = __SWR | __SSTR;
f._bf._size = 0;
f._w = rb_str_capacity(str);
f._bf._base = (unsigned char *)str;
f._p = (unsigned char *)RSTRING_END(str);
klass = RBASIC(str)->klass;
- RBASIC(str)->klass = 0;
+ RBASIC_CLEAR_CLASS(str);
f.vwrite = ruby__sfvwrite;
+ f.vextra = ruby__sfvextra;
+ buffer.value = 0;
BSD_vfprintf(&f, fmt, ap);
- RBASIC(str)->klass = klass;
+ RBASIC_SET_CLASS_RAW(str, klass);
rb_str_resize(str, (char *)f._p - RSTRING_PTR(str));
+#undef f
return str;
}
VALUE