sprintf.c

Go to the documentation of this file.
00001 /**********************************************************************
00002 
00003   sprintf.c -
00004 
00005   $Author: nagachika $
00006   created at: Fri Oct 15 10:39:26 JST 1993
00007 
00008   Copyright (C) 1993-2007 Yukihiro Matsumoto
00009   Copyright (C) 2000  Network Applied Communication Laboratory, Inc.
00010   Copyright (C) 2000  Information-technology Promotion Agency, Japan
00011 
00012 **********************************************************************/
00013 
00014 #include "ruby/ruby.h"
00015 #include "ruby/re.h"
00016 #include "ruby/encoding.h"
00017 #include "internal.h"
00018 #include <math.h>
00019 #include <stdarg.h>
00020 
00021 #ifdef HAVE_IEEEFP_H
00022 #include <ieeefp.h>
00023 #endif
00024 
00025 #define BIT_DIGITS(N)   (((N)*146)/485 + 1)  /* log2(10) =~ 146/485 */
00026 
00027 extern const char ruby_digitmap[];
00028 
00029 static void fmt_setup(char*,size_t,int,int,int,int);
00030 
00031 static char
00032 sign_bits(int base, const char *p)
00033 {
00034     char c = '.';
00035 
00036     switch (base) {
00037       case 16:
00038         if (*p == 'X') c = 'F';
00039         else c = 'f';
00040         break;
00041       case 8:
00042         c = '7'; break;
00043       case 2:
00044         c = '1'; break;
00045     }
00046     return c;
00047 }
00048 
00049 #define FNONE  0
00050 #define FSHARP 1
00051 #define FMINUS 2
00052 #define FPLUS  4
00053 #define FZERO  8
00054 #define FSPACE 16
00055 #define FWIDTH 32
00056 #define FPREC  64
00057 #define FPREC0 128
00058 
00059 #define CHECK(l) do {\
00060     int cr = ENC_CODERANGE(result);\
00061     while (blen + (l) >= bsiz) {\
00062         bsiz*=2;\
00063     }\
00064     rb_str_resize(result, bsiz);\
00065     ENC_CODERANGE_SET(result, cr);\
00066     buf = RSTRING_PTR(result);\
00067 } while (0)
00068 
00069 #define PUSH(s, l) do { \
00070     CHECK(l);\
00071     memcpy(&buf[blen], (s), (l));\
00072     blen += (l);\
00073 } while (0)
00074 
00075 #define FILL(c, l) do { \
00076     CHECK(l);\
00077     memset(&buf[blen], (c), (l));\
00078     blen += (l);\
00079 } while (0)
00080 
00081 #define GETARG() (nextvalue != Qundef ? nextvalue : \
00082                   GETNEXTARG())
00083 
00084 #define GETNEXTARG() ( \
00085     posarg == -1 ? \
00086     (rb_raise(rb_eArgError, "unnumbered(%d) mixed with numbered", nextarg), 0) : \
00087     posarg == -2 ? \
00088     (rb_raise(rb_eArgError, "unnumbered(%d) mixed with named", nextarg), 0) : \
00089     (posarg = nextarg++, GETNTHARG(posarg)))
00090 
00091 #define GETPOSARG(n) (posarg > 0 ? \
00092     (rb_raise(rb_eArgError, "numbered(%d) after unnumbered(%d)", (n), posarg), 0) : \
00093     posarg == -2 ? \
00094     (rb_raise(rb_eArgError, "numbered(%d) after named", (n)), 0) : \
00095     (((n) < 1) ? (rb_raise(rb_eArgError, "invalid index - %d$", (n)), 0) : \
00096                (posarg = -1, GETNTHARG(n))))
00097 
00098 #define GETNTHARG(nth) \
00099     (((nth) >= argc) ? (rb_raise(rb_eArgError, "too few arguments"), 0) : argv[(nth)])
00100 
00101 #define GETNAMEARG(id, name, len, enc) ( \
00102     posarg > 0 ? \
00103     (rb_enc_raise((enc), rb_eArgError, "named%.*s after unnumbered(%d)", (len), (name), posarg), 0) : \
00104     posarg == -1 ? \
00105     (rb_enc_raise((enc), rb_eArgError, "named%.*s after numbered", (len), (name)), 0) : \
00106     (posarg = -2, rb_hash_lookup2(get_hash(&hash, argc, argv), (id), Qundef)))
00107 
00108 #define GETNUM(n, val) \
00109     for (; p < end && rb_enc_isdigit(*p, enc); p++) {   \
00110         int next_n = (n); \
00111         if (MUL_OVERFLOW_INT_P(10, next_n)) \
00112             rb_raise(rb_eArgError, #val " too big"); \
00113         next_n *= 10; \
00114         if (INT_MAX - (*p - '0') < next_n) \
00115             rb_raise(rb_eArgError, #val " too big"); \
00116         next_n += *p - '0'; \
00117         (n) = next_n; \
00118     } \
00119     if (p >= end) { \
00120         rb_raise(rb_eArgError, "malformed format string - %%*[0-9]"); \
00121     }
00122 
00123 #define GETASTER(val) do { \
00124     t = p++; \
00125     n = 0; \
00126     GETNUM(n, (val)); \
00127     if (*p == '$') { \
00128         tmp = GETPOSARG(n); \
00129     } \
00130     else { \
00131         tmp = GETNEXTARG(); \
00132         p = t; \
00133     } \
00134     (val) = NUM2INT(tmp); \
00135 } while (0)
00136 
00137 static VALUE
00138 get_hash(volatile VALUE *hash, int argc, const VALUE *argv)
00139 {
00140     VALUE tmp;
00141 
00142     if (*hash != Qundef) return *hash;
00143     if (argc != 2) {
00144         rb_raise(rb_eArgError, "one hash required");
00145     }
00146     tmp = rb_check_hash_type(argv[1]);
00147     if (NIL_P(tmp)) {
00148         rb_raise(rb_eArgError, "one hash required");
00149     }
00150     return (*hash = tmp);
00151 }
00152 
00153 /*
00154  *  call-seq:
00155  *     format(format_string [, arguments...] )   -> string
00156  *     sprintf(format_string [, arguments...] )  -> string
00157  *
00158  *  Returns the string resulting from applying <i>format_string</i> to
00159  *  any additional arguments.  Within the format string, any characters
00160  *  other than format sequences are copied to the result.
00161  *
00162  *  The syntax of a format sequence is follows.
00163  *
00164  *    %[flags][width][.precision]type
00165  *
00166  *  A format
00167  *  sequence consists of a percent sign, followed by optional flags,
00168  *  width, and precision indicators, then terminated with a field type
00169  *  character.  The field type controls how the corresponding
00170  *  <code>sprintf</code> argument is to be interpreted, while the flags
00171  *  modify that interpretation.
00172  *
00173  *  The field type characters are:
00174  *
00175  *      Field |  Integer Format
00176  *      ------+--------------------------------------------------------------
00177  *        b   | Convert argument as a binary number.
00178  *            | Negative numbers will be displayed as a two's complement
00179  *            | prefixed with `..1'.
00180  *        B   | Equivalent to `b', but uses an uppercase 0B for prefix
00181  *            | in the alternative format by #.
00182  *        d   | Convert argument as a decimal number.
00183  *        i   | Identical to `d'.
00184  *        o   | Convert argument as an octal number.
00185  *            | Negative numbers will be displayed as a two's complement
00186  *            | prefixed with `..7'.
00187  *        u   | Identical to `d'.
00188  *        x   | Convert argument as a hexadecimal number.
00189  *            | Negative numbers will be displayed as a two's complement
00190  *            | prefixed with `..f' (representing an infinite string of
00191  *            | leading 'ff's).
00192  *        X   | Equivalent to `x', but uses uppercase letters.
00193  *
00194  *      Field |  Float Format
00195  *      ------+--------------------------------------------------------------
00196  *        e   | Convert floating point argument into exponential notation
00197  *            | with one digit before the decimal point as [-]d.dddddde[+-]dd.
00198  *            | The precision specifies the number of digits after the decimal
00199  *            | point (defaulting to six).
00200  *        E   | Equivalent to `e', but uses an uppercase E to indicate
00201  *            | the exponent.
00202  *        f   | Convert floating point argument as [-]ddd.dddddd,
00203  *            | where the precision specifies the number of digits after
00204  *            | the decimal point.
00205  *        g   | Convert a floating point number using exponential form
00206  *            | if the exponent is less than -4 or greater than or
00207  *            | equal to the precision, or in dd.dddd form otherwise.
00208  *            | The precision specifies the number of significant digits.
00209  *        G   | Equivalent to `g', but use an uppercase `E' in exponent form.
00210  *        a   | Convert floating point argument as [-]0xh.hhhhp[+-]dd,
00211  *            | which is consisted from optional sign, "0x", fraction part
00212  *            | as hexadecimal, "p", and exponential part as decimal.
00213  *        A   | Equivalent to `a', but use uppercase `X' and `P'.
00214  *
00215  *      Field |  Other Format
00216  *      ------+--------------------------------------------------------------
00217  *        c   | Argument is the numeric code for a single character or
00218  *            | a single character string itself.
00219  *        p   | The valuing of argument.inspect.
00220  *        s   | Argument is a string to be substituted.  If the format
00221  *            | sequence contains a precision, at most that many characters
00222  *            | will be copied.
00223  *        %   | A percent sign itself will be displayed.  No argument taken.
00224  *
00225  *  The flags modifies the behavior of the formats.
00226  *  The flag characters are:
00227  *
00228  *    Flag     | Applies to    | Meaning
00229  *    ---------+---------------+-----------------------------------------
00230  *    space    | bBdiouxX      | Leave a space at the start of
00231  *             | aAeEfgG       | non-negative numbers.
00232  *             | (numeric fmt) | For `o', `x', `X', `b' and `B', use
00233  *             |               | a minus sign with absolute value for
00234  *             |               | negative values.
00235  *    ---------+---------------+-----------------------------------------
00236  *    (digit)$ | all           | Specifies the absolute argument number
00237  *             |               | for this field.  Absolute and relative
00238  *             |               | argument numbers cannot be mixed in a
00239  *             |               | sprintf string.
00240  *    ---------+---------------+-----------------------------------------
00241  *     #       | bBoxX         | Use an alternative format.
00242  *             | aAeEfgG       | For the conversions `o', increase the precision
00243  *             |               | until the first digit will be `0' if
00244  *             |               | it is not formatted as complements.
00245  *             |               | For the conversions `x', `X', `b' and `B'
00246  *             |               | on non-zero, prefix the result with ``0x'',
00247  *             |               | ``0X'', ``0b'' and ``0B'', respectively.
00248  *             |               | For `a', `A', `e', `E', `f', `g', and 'G',
00249  *             |               | force a decimal point to be added,
00250  *             |               | even if no digits follow.
00251  *             |               | For `g' and 'G', do not remove trailing zeros.
00252  *    ---------+---------------+-----------------------------------------
00253  *    +        | bBdiouxX      | Add a leading plus sign to non-negative
00254  *             | aAeEfgG       | numbers.
00255  *             | (numeric fmt) | For `o', `x', `X', `b' and `B', use
00256  *             |               | a minus sign with absolute value for
00257  *             |               | negative values.
00258  *    ---------+---------------+-----------------------------------------
00259  *    -        | all           | Left-justify the result of this conversion.
00260  *    ---------+---------------+-----------------------------------------
00261  *    0 (zero) | bBdiouxX      | Pad with zeros, not spaces.
00262  *             | aAeEfgG       | For `o', `x', `X', `b' and `B', radix-1
00263  *             | (numeric fmt) | is used for negative numbers formatted as
00264  *             |               | complements.
00265  *    ---------+---------------+-----------------------------------------
00266  *    *        | all           | Use the next argument as the field width.
00267  *             |               | If negative, left-justify the result. If the
00268  *             |               | asterisk is followed by a number and a dollar
00269  *             |               | sign, use the indicated argument as the width.
00270  *
00271  *  Examples of flags:
00272  *
00273  *   # `+' and space flag specifies the sign of non-negative numbers.
00274  *   sprintf("%d", 123)  #=> "123"
00275  *   sprintf("%+d", 123) #=> "+123"
00276  *   sprintf("% d", 123) #=> " 123"
00277  *
00278  *   # `#' flag for `o' increases number of digits to show `0'.
00279  *   # `+' and space flag changes format of negative numbers.
00280  *   sprintf("%o", 123)   #=> "173"
00281  *   sprintf("%#o", 123)  #=> "0173"
00282  *   sprintf("%+o", -123) #=> "-173"
00283  *   sprintf("%o", -123)  #=> "..7605"
00284  *   sprintf("%#o", -123) #=> "..7605"
00285  *
00286  *   # `#' flag for `x' add a prefix `0x' for non-zero numbers.
00287  *   # `+' and space flag disables complements for negative numbers.
00288  *   sprintf("%x", 123)   #=> "7b"
00289  *   sprintf("%#x", 123)  #=> "0x7b"
00290  *   sprintf("%+x", -123) #=> "-7b"
00291  *   sprintf("%x", -123)  #=> "..f85"
00292  *   sprintf("%#x", -123) #=> "0x..f85"
00293  *   sprintf("%#x", 0)    #=> "0"
00294  *
00295  *   # `#' for `X' uses the prefix `0X'.
00296  *   sprintf("%X", 123)  #=> "7B"
00297  *   sprintf("%#X", 123) #=> "0X7B"
00298  *
00299  *   # `#' flag for `b' add a prefix `0b' for non-zero numbers.
00300  *   # `+' and space flag disables complements for negative numbers.
00301  *   sprintf("%b", 123)   #=> "1111011"
00302  *   sprintf("%#b", 123)  #=> "0b1111011"
00303  *   sprintf("%+b", -123) #=> "-1111011"
00304  *   sprintf("%b", -123)  #=> "..10000101"
00305  *   sprintf("%#b", -123) #=> "0b..10000101"
00306  *   sprintf("%#b", 0)    #=> "0"
00307  *
00308  *   # `#' for `B' uses the prefix `0B'.
00309  *   sprintf("%B", 123)  #=> "1111011"
00310  *   sprintf("%#B", 123) #=> "0B1111011"
00311  *
00312  *   # `#' for `e' forces to show the decimal point.
00313  *   sprintf("%.0e", 1)  #=> "1e+00"
00314  *   sprintf("%#.0e", 1) #=> "1.e+00"
00315  *
00316  *   # `#' for `f' forces to show the decimal point.
00317  *   sprintf("%.0f", 1234)  #=> "1234"
00318  *   sprintf("%#.0f", 1234) #=> "1234."
00319  *
00320  *   # `#' for `g' forces to show the decimal point.
00321  *   # It also disables stripping lowest zeros.
00322  *   sprintf("%g", 123.4)   #=> "123.4"
00323  *   sprintf("%#g", 123.4)  #=> "123.400"
00324  *   sprintf("%g", 123456)  #=> "123456"
00325  *   sprintf("%#g", 123456) #=> "123456."
00326  *
00327  *  The field width is an optional integer, followed optionally by a
00328  *  period and a precision.  The width specifies the minimum number of
00329  *  characters that will be written to the result for this field.
00330  *
00331  *  Examples of width:
00332  *
00333  *   # padding is done by spaces,       width=20
00334  *   # 0 or radix-1.             <------------------>
00335  *   sprintf("%20d", 123)   #=> "                 123"
00336  *   sprintf("%+20d", 123)  #=> "                +123"
00337  *   sprintf("%020d", 123)  #=> "00000000000000000123"
00338  *   sprintf("%+020d", 123) #=> "+0000000000000000123"
00339  *   sprintf("% 020d", 123) #=> " 0000000000000000123"
00340  *   sprintf("%-20d", 123)  #=> "123                 "
00341  *   sprintf("%-+20d", 123) #=> "+123                "
00342  *   sprintf("%- 20d", 123) #=> " 123                "
00343  *   sprintf("%020x", -123) #=> "..ffffffffffffffff85"
00344  *
00345  *  For
00346  *  numeric fields, the precision controls the number of decimal places
00347  *  displayed.  For string fields, the precision determines the maximum
00348  *  number of characters to be copied from the string.  (Thus, the format
00349  *  sequence <code>%10.10s</code> will always contribute exactly ten
00350  *  characters to the result.)
00351  *
00352  *  Examples of precisions:
00353  *
00354  *   # precision for `d', 'o', 'x' and 'b' is
00355  *   # minimum number of digits               <------>
00356  *   sprintf("%20.8d", 123)  #=> "            00000123"
00357  *   sprintf("%20.8o", 123)  #=> "            00000173"
00358  *   sprintf("%20.8x", 123)  #=> "            0000007b"
00359  *   sprintf("%20.8b", 123)  #=> "            01111011"
00360  *   sprintf("%20.8d", -123) #=> "           -00000123"
00361  *   sprintf("%20.8o", -123) #=> "            ..777605"
00362  *   sprintf("%20.8x", -123) #=> "            ..ffff85"
00363  *   sprintf("%20.8b", -11)  #=> "            ..110101"
00364  *
00365  *   # "0x" and "0b" for `#x' and `#b' is not counted for
00366  *   # precision but "0" for `#o' is counted.  <------>
00367  *   sprintf("%#20.8d", 123)  #=> "            00000123"
00368  *   sprintf("%#20.8o", 123)  #=> "            00000173"
00369  *   sprintf("%#20.8x", 123)  #=> "          0x0000007b"
00370  *   sprintf("%#20.8b", 123)  #=> "          0b01111011"
00371  *   sprintf("%#20.8d", -123) #=> "           -00000123"
00372  *   sprintf("%#20.8o", -123) #=> "            ..777605"
00373  *   sprintf("%#20.8x", -123) #=> "          0x..ffff85"
00374  *   sprintf("%#20.8b", -11)  #=> "          0b..110101"
00375  *
00376  *   # precision for `e' is number of
00377  *   # digits after the decimal point           <------>
00378  *   sprintf("%20.8e", 1234.56789) #=> "      1.23456789e+03"
00379  *
00380  *   # precision for `f' is number of
00381  *   # digits after the decimal point               <------>
00382  *   sprintf("%20.8f", 1234.56789) #=> "       1234.56789000"
00383  *
00384  *   # precision for `g' is number of
00385  *   # significant digits                          <------->
00386  *   sprintf("%20.8g", 1234.56789) #=> "           1234.5679"
00387  *
00388  *   #                                         <------->
00389  *   sprintf("%20.8g", 123456789)  #=> "       1.2345679e+08"
00390  *
00391  *   # precision for `s' is
00392  *   # maximum number of characters                    <------>
00393  *   sprintf("%20.8s", "string test") #=> "            string t"
00394  *
00395  *  Examples:
00396  *
00397  *     sprintf("%d %04x", 123, 123)               #=> "123 007b"
00398  *     sprintf("%08b '%4s'", 123, 123)            #=> "01111011 ' 123'"
00399  *     sprintf("%1$*2$s %2$d %1$s", "hello", 8)   #=> "   hello 8 hello"
00400  *     sprintf("%1$*2$s %2$d", "hello", -8)       #=> "hello    -8"
00401  *     sprintf("%+g:% g:%-g", 1.23, 1.23, 1.23)   #=> "+1.23: 1.23:1.23"
00402  *     sprintf("%u", -123)                        #=> "-123"
00403  *
00404  *  For more complex formatting, Ruby supports a reference by name.
00405  *  %<name>s style uses format style, but %{name} style doesn't.
00406  *
00407  *  Examples:
00408  *    sprintf("%<foo>d : %<bar>f", { :foo => 1, :bar => 2 })
00409  *      #=> 1 : 2.000000
00410  *    sprintf("%{foo}f", { :foo => 1 })
00411  *      # => "1f"
00412  */
00413 
00414 VALUE
00415 rb_f_sprintf(int argc, const VALUE *argv)
00416 {
00417     return rb_str_format(argc - 1, argv + 1, GETNTHARG(0));
00418 }
00419 
00420 VALUE
00421 rb_str_format(int argc, const VALUE *argv, VALUE fmt)
00422 {
00423     rb_encoding *enc;
00424     const char *p, *end;
00425     char *buf;
00426     long blen, bsiz;
00427     VALUE result;
00428 
00429     long scanned = 0;
00430     int coderange = ENC_CODERANGE_7BIT;
00431     int width, prec, flags = FNONE;
00432     int nextarg = 1;
00433     int posarg = 0;
00434     int tainted = 0;
00435     VALUE nextvalue;
00436     VALUE tmp;
00437     VALUE str;
00438     volatile VALUE hash = Qundef;
00439 
00440 #define CHECK_FOR_WIDTH(f)                               \
00441     if ((f) & FWIDTH) {                                  \
00442         rb_raise(rb_eArgError, "width given twice");     \
00443     }                                                    \
00444     if ((f) & FPREC0) {                                  \
00445         rb_raise(rb_eArgError, "width after precision"); \
00446     }
00447 #define CHECK_FOR_FLAGS(f)                               \
00448     if ((f) & FWIDTH) {                                  \
00449         rb_raise(rb_eArgError, "flag after width");      \
00450     }                                                    \
00451     if ((f) & FPREC0) {                                  \
00452         rb_raise(rb_eArgError, "flag after precision"); \
00453     }
00454 
00455     ++argc;
00456     --argv;
00457     if (OBJ_TAINTED(fmt)) tainted = 1;
00458     StringValue(fmt);
00459     enc = rb_enc_get(fmt);
00460     fmt = rb_str_new4(fmt);
00461     p = RSTRING_PTR(fmt);
00462     end = p + RSTRING_LEN(fmt);
00463     blen = 0;
00464     bsiz = 120;
00465     result = rb_str_buf_new(bsiz);
00466     rb_enc_copy(result, fmt);
00467     buf = RSTRING_PTR(result);
00468     memset(buf, 0, bsiz);
00469     ENC_CODERANGE_SET(result, coderange);
00470 
00471     for (; p < end; p++) {
00472         const char *t;
00473         int n;
00474         ID id = 0;
00475 
00476         for (t = p; t < end && *t != '%'; t++) ;
00477         PUSH(p, t - p);
00478         if (coderange != ENC_CODERANGE_BROKEN && scanned < blen) {
00479             scanned += rb_str_coderange_scan_restartable(buf+scanned, buf+blen, enc, &coderange);
00480             ENC_CODERANGE_SET(result, coderange);
00481         }
00482         if (t >= end) {
00483             /* end of fmt string */
00484             goto sprint_exit;
00485         }
00486         p = t + 1;              /* skip `%' */
00487 
00488         width = prec = -1;
00489         nextvalue = Qundef;
00490       retry:
00491         switch (*p) {
00492           default:
00493             if (rb_enc_isprint(*p, enc))
00494                 rb_raise(rb_eArgError, "malformed format string - %%%c", *p);
00495             else
00496                 rb_raise(rb_eArgError, "malformed format string");
00497             break;
00498 
00499           case ' ':
00500             CHECK_FOR_FLAGS(flags);
00501             flags |= FSPACE;
00502             p++;
00503             goto retry;
00504 
00505           case '#':
00506             CHECK_FOR_FLAGS(flags);
00507             flags |= FSHARP;
00508             p++;
00509             goto retry;
00510 
00511           case '+':
00512             CHECK_FOR_FLAGS(flags);
00513             flags |= FPLUS;
00514             p++;
00515             goto retry;
00516 
00517           case '-':
00518             CHECK_FOR_FLAGS(flags);
00519             flags |= FMINUS;
00520             p++;
00521             goto retry;
00522 
00523           case '0':
00524             CHECK_FOR_FLAGS(flags);
00525             flags |= FZERO;
00526             p++;
00527             goto retry;
00528 
00529           case '1': case '2': case '3': case '4':
00530           case '5': case '6': case '7': case '8': case '9':
00531             n = 0;
00532             GETNUM(n, width);
00533             if (*p == '$') {
00534                 if (nextvalue != Qundef) {
00535                     rb_raise(rb_eArgError, "value given twice - %d$", n);
00536                 }
00537                 nextvalue = GETPOSARG(n);
00538                 p++;
00539                 goto retry;
00540             }
00541             CHECK_FOR_WIDTH(flags);
00542             width = n;
00543             flags |= FWIDTH;
00544             goto retry;
00545 
00546           case '<':
00547           case '{':
00548             {
00549                 const char *start = p;
00550                 char term = (*p == '<') ? '>' : '}';
00551                 int len;
00552 
00553                 for (; p < end && *p != term; ) {
00554                     p += rb_enc_mbclen(p, end, enc);
00555                 }
00556                 if (p >= end) {
00557                     rb_raise(rb_eArgError, "malformed name - unmatched parenthesis");
00558                 }
00559 #if SIZEOF_INT < SIZEOF_SIZE_T
00560                 if ((size_t)(p - start) >= INT_MAX) {
00561                     const int message_limit = 20;
00562                     len = (int)(rb_enc_right_char_head(start, start + message_limit, p, enc) - start);
00563                     rb_enc_raise(enc, rb_eArgError,
00564                                  "too long name (%"PRIdSIZE" bytes) - %.*s...%c",
00565                                  (size_t)(p - start - 2), len, start, term);
00566                 }
00567 #endif
00568                 len = (int)(p - start + 1); /* including parenthesis */
00569                 if (id) {
00570                     rb_enc_raise(enc, rb_eArgError, "named%.*s after <%s>",
00571                                  len, start, rb_id2name(id));
00572                 }
00573                 nextvalue = GETNAMEARG((id = rb_check_id_cstr(start + 1,
00574                                                               len - 2 /* without parenthesis */,
00575                                                               enc),
00576                                         ID2SYM(id)),
00577                                        start, len, enc);
00578                 if (nextvalue == Qundef) {
00579                     rb_enc_raise(enc, rb_eKeyError, "key%.*s not found", len, start);
00580                 }
00581                 if (term == '}') goto format_s;
00582                 p++;
00583                 goto retry;
00584             }
00585 
00586           case '*':
00587             CHECK_FOR_WIDTH(flags);
00588             flags |= FWIDTH;
00589             GETASTER(width);
00590             if (width < 0) {
00591                 flags |= FMINUS;
00592                 width = -width;
00593             }
00594             p++;
00595             goto retry;
00596 
00597           case '.':
00598             if (flags & FPREC0) {
00599                 rb_raise(rb_eArgError, "precision given twice");
00600             }
00601             flags |= FPREC|FPREC0;
00602 
00603             prec = 0;
00604             p++;
00605             if (*p == '*') {
00606                 GETASTER(prec);
00607                 if (prec < 0) { /* ignore negative precision */
00608                     flags &= ~FPREC;
00609                 }
00610                 p++;
00611                 goto retry;
00612             }
00613 
00614             GETNUM(prec, precision);
00615             goto retry;
00616 
00617           case '\n':
00618           case '\0':
00619             p--;
00620           case '%':
00621             if (flags != FNONE) {
00622                 rb_raise(rb_eArgError, "invalid format character - %%");
00623             }
00624             PUSH("%", 1);
00625             break;
00626 
00627           case 'c':
00628             {
00629                 VALUE val = GETARG();
00630                 VALUE tmp;
00631                 unsigned int c;
00632                 int n;
00633 
00634                 tmp = rb_check_string_type(val);
00635                 if (!NIL_P(tmp)) {
00636                     if (rb_enc_strlen(RSTRING_PTR(tmp),RSTRING_END(tmp),enc) != 1) {
00637                         rb_raise(rb_eArgError, "%%c requires a character");
00638                     }
00639                     c = rb_enc_codepoint_len(RSTRING_PTR(tmp), RSTRING_END(tmp), &n, enc);
00640                     RB_GC_GUARD(tmp);
00641                 }
00642                 else {
00643                     c = NUM2INT(val);
00644                     n = rb_enc_codelen(c, enc);
00645                 }
00646                 if (n <= 0) {
00647                     rb_raise(rb_eArgError, "invalid character");
00648                 }
00649                 if (!(flags & FWIDTH)) {
00650                     CHECK(n);
00651                     rb_enc_mbcput(c, &buf[blen], enc);
00652                     blen += n;
00653                 }
00654                 else if ((flags & FMINUS)) {
00655                     CHECK(n);
00656                     rb_enc_mbcput(c, &buf[blen], enc);
00657                     blen += n;
00658                     FILL(' ', width-1);
00659                 }
00660                 else {
00661                     FILL(' ', width-1);
00662                     CHECK(n);
00663                     rb_enc_mbcput(c, &buf[blen], enc);
00664                     blen += n;
00665                 }
00666             }
00667             break;
00668 
00669           case 's':
00670           case 'p':
00671           format_s:
00672             {
00673                 VALUE arg = GETARG();
00674                 long len, slen;
00675 
00676                 if (*p == 'p') arg = rb_inspect(arg);
00677                 str = rb_obj_as_string(arg);
00678                 if (OBJ_TAINTED(str)) tainted = 1;
00679                 len = RSTRING_LEN(str);
00680                 rb_str_set_len(result, blen);
00681                 if (coderange != ENC_CODERANGE_BROKEN && scanned < blen) {
00682                     int cr = coderange;
00683                     scanned += rb_str_coderange_scan_restartable(buf+scanned, buf+blen, enc, &cr);
00684                     ENC_CODERANGE_SET(result,
00685                                       (cr == ENC_CODERANGE_UNKNOWN ?
00686                                        ENC_CODERANGE_BROKEN : (coderange = cr)));
00687                 }
00688                 enc = rb_enc_check(result, str);
00689                 if (flags&(FPREC|FWIDTH)) {
00690                     slen = rb_enc_strlen(RSTRING_PTR(str),RSTRING_END(str),enc);
00691                     if (slen < 0) {
00692                         rb_raise(rb_eArgError, "invalid mbstring sequence");
00693                     }
00694                     if ((flags&FPREC) && (prec < slen)) {
00695                         char *p = rb_enc_nth(RSTRING_PTR(str), RSTRING_END(str),
00696                                              prec, enc);
00697                         slen = prec;
00698                         len = p - RSTRING_PTR(str);
00699                     }
00700                     /* need to adjust multi-byte string pos */
00701                     if ((flags&FWIDTH) && (width > slen)) {
00702                         width -= (int)slen;
00703                         if (!(flags&FMINUS)) {
00704                             CHECK(width);
00705                             while (width--) {
00706                                 buf[blen++] = ' ';
00707                             }
00708                         }
00709                         CHECK(len);
00710                         memcpy(&buf[blen], RSTRING_PTR(str), len);
00711                         RB_GC_GUARD(str);
00712                         blen += len;
00713                         if (flags&FMINUS) {
00714                             CHECK(width);
00715                             while (width--) {
00716                                 buf[blen++] = ' ';
00717                             }
00718                         }
00719                         rb_enc_associate(result, enc);
00720                         break;
00721                     }
00722                 }
00723                 PUSH(RSTRING_PTR(str), len);
00724                 RB_GC_GUARD(str);
00725                 rb_enc_associate(result, enc);
00726             }
00727             break;
00728 
00729           case 'd':
00730           case 'i':
00731           case 'o':
00732           case 'x':
00733           case 'X':
00734           case 'b':
00735           case 'B':
00736           case 'u':
00737             {
00738                 volatile VALUE val = GETARG();
00739                 int valsign;
00740                 char nbuf[64], *s;
00741                 const char *prefix = 0;
00742                 int sign = 0, dots = 0;
00743                 char sc = 0;
00744                 long v = 0;
00745                 int base, bignum = 0;
00746                 int len;
00747 
00748                 switch (*p) {
00749                   case 'd':
00750                   case 'i':
00751                   case 'u':
00752                     sign = 1; break;
00753                   case 'o':
00754                   case 'x':
00755                   case 'X':
00756                   case 'b':
00757                   case 'B':
00758                     if (flags&(FPLUS|FSPACE)) sign = 1;
00759                     break;
00760                 }
00761                 if (flags & FSHARP) {
00762                     switch (*p) {
00763                       case 'o':
00764                         prefix = "0"; break;
00765                       case 'x':
00766                         prefix = "0x"; break;
00767                       case 'X':
00768                         prefix = "0X"; break;
00769                       case 'b':
00770                         prefix = "0b"; break;
00771                       case 'B':
00772                         prefix = "0B"; break;
00773                     }
00774                 }
00775 
00776               bin_retry:
00777                 switch (TYPE(val)) {
00778                   case T_FLOAT:
00779                     if (FIXABLE(RFLOAT_VALUE(val))) {
00780                         val = LONG2FIX((long)RFLOAT_VALUE(val));
00781                         goto bin_retry;
00782                     }
00783                     val = rb_dbl2big(RFLOAT_VALUE(val));
00784                     if (FIXNUM_P(val)) goto bin_retry;
00785                     bignum = 1;
00786                     break;
00787                   case T_STRING:
00788                     val = rb_str_to_inum(val, 0, TRUE);
00789                     goto bin_retry;
00790                   case T_BIGNUM:
00791                     bignum = 1;
00792                     break;
00793                   case T_FIXNUM:
00794                     v = FIX2LONG(val);
00795                     break;
00796                   default:
00797                     val = rb_Integer(val);
00798                     goto bin_retry;
00799                 }
00800 
00801                 switch (*p) {
00802                   case 'o':
00803                     base = 8; break;
00804                   case 'x':
00805                   case 'X':
00806                     base = 16; break;
00807                   case 'b':
00808                   case 'B':
00809                     base = 2; break;
00810                   case 'u':
00811                   case 'd':
00812                   case 'i':
00813                   default:
00814                     base = 10; break;
00815                 }
00816 
00817                 if (base != 10) {
00818                     int numbits = ffs(base)-1;
00819                     size_t abs_nlz_bits;
00820                     size_t numdigits = rb_absint_numwords(val, numbits, &abs_nlz_bits);
00821                     long i;
00822                     if (INT_MAX-1 < numdigits) /* INT_MAX is used because rb_long2int is used later. */
00823                         rb_raise(rb_eArgError, "size too big");
00824                     if (sign) {
00825                         if (numdigits == 0)
00826                             numdigits = 1;
00827                         tmp = rb_str_new(NULL, numdigits);
00828                         valsign = rb_integer_pack(val, RSTRING_PTR(tmp), RSTRING_LEN(tmp),
00829                                 1, CHAR_BIT-numbits, INTEGER_PACK_BIG_ENDIAN);
00830                         for (i = 0; i < RSTRING_LEN(tmp); i++)
00831                             RSTRING_PTR(tmp)[i] = ruby_digitmap[((unsigned char *)RSTRING_PTR(tmp))[i]];
00832                         s = RSTRING_PTR(tmp);
00833                         if (valsign < 0) {
00834                             sc = '-';
00835                             width--;
00836                         }
00837                         else if (flags & FPLUS) {
00838                             sc = '+';
00839                             width--;
00840                         }
00841                         else if (flags & FSPACE) {
00842                             sc = ' ';
00843                             width--;
00844                         }
00845                     }
00846                     else {
00847                         /* Following conditional "numdigits++" guarantees the
00848                          * most significant digit as
00849                          * - '1'(bin), '7'(oct) or 'f'(hex) for negative numbers
00850                          * - '0' for zero
00851                          * - not '0' for positive numbers.
00852                          *
00853                          * It also guarantees the most significant two
00854                          * digits will not be '11'(bin), '77'(oct), 'ff'(hex)
00855                          * or '00'.  */
00856                         if (numdigits == 0 ||
00857                                 ((abs_nlz_bits != (size_t)(numbits-1) ||
00858                                   !rb_absint_singlebit_p(val)) &&
00859                                  (!bignum ? v < 0 : RBIGNUM_NEGATIVE_P(val))))
00860                             numdigits++;
00861                         tmp = rb_str_new(NULL, numdigits);
00862                         valsign = rb_integer_pack(val, RSTRING_PTR(tmp), RSTRING_LEN(tmp),
00863                                 1, CHAR_BIT-numbits, INTEGER_PACK_2COMP | INTEGER_PACK_BIG_ENDIAN);
00864                         for (i = 0; i < RSTRING_LEN(tmp); i++)
00865                             RSTRING_PTR(tmp)[i] = ruby_digitmap[((unsigned char *)RSTRING_PTR(tmp))[i]];
00866                         s = RSTRING_PTR(tmp);
00867                         dots = valsign < 0;
00868                     }
00869                     len = rb_long2int(RSTRING_END(tmp) - s);
00870                 }
00871                 else if (!bignum) {
00872                     valsign = 1;
00873                     if (v < 0) {
00874                         v = -v;
00875                         sc = '-';
00876                         width--;
00877                         valsign = -1;
00878                     }
00879                     else if (flags & FPLUS) {
00880                         sc = '+';
00881                         width--;
00882                     }
00883                     else if (flags & FSPACE) {
00884                         sc = ' ';
00885                         width--;
00886                     }
00887                     snprintf(nbuf, sizeof(nbuf), "%ld", v);
00888                     s = nbuf;
00889                     len = (int)strlen(s);
00890                 }
00891                 else {
00892                     tmp = rb_big2str(val, 10);
00893                     s = RSTRING_PTR(tmp);
00894                     valsign = 1;
00895                     if (s[0] == '-') {
00896                         s++;
00897                         sc = '-';
00898                         width--;
00899                         valsign = -1;
00900                     }
00901                     else if (flags & FPLUS) {
00902                         sc = '+';
00903                         width--;
00904                     }
00905                     else if (flags & FSPACE) {
00906                         sc = ' ';
00907                         width--;
00908                     }
00909                     len = rb_long2int(RSTRING_END(tmp) - s);
00910                 }
00911 
00912                 if (dots) {
00913                     prec -= 2;
00914                     width -= 2;
00915                 }
00916 
00917                 if (*p == 'X') {
00918                     char *pp = s;
00919                     int c;
00920                     while ((c = (int)(unsigned char)*pp) != 0) {
00921                         *pp = rb_enc_toupper(c, enc);
00922                         pp++;
00923                     }
00924                 }
00925                 if (prefix && !prefix[1]) { /* octal */
00926                     if (dots) {
00927                         prefix = 0;
00928                     }
00929                     else if (len == 1 && *s == '0') {
00930                         len = 0;
00931                         if (flags & FPREC) prec--;
00932                     }
00933                     else if ((flags & FPREC) && (prec > len)) {
00934                         prefix = 0;
00935                     }
00936                 }
00937                 else if (len == 1 && *s == '0') {
00938                     prefix = 0;
00939                 }
00940                 if (prefix) {
00941                     width -= (int)strlen(prefix);
00942                 }
00943                 if ((flags & (FZERO|FMINUS|FPREC)) == FZERO) {
00944                     prec = width;
00945                     width = 0;
00946                 }
00947                 else {
00948                     if (prec < len) {
00949                         if (!prefix && prec == 0 && len == 1 && *s == '0') len = 0;
00950                         prec = len;
00951                     }
00952                     width -= prec;
00953                 }
00954                 if (!(flags&FMINUS)) {
00955                     CHECK(width);
00956                     while (width-- > 0) {
00957                         buf[blen++] = ' ';
00958                     }
00959                 }
00960                 if (sc) PUSH(&sc, 1);
00961                 if (prefix) {
00962                     int plen = (int)strlen(prefix);
00963                     PUSH(prefix, plen);
00964                 }
00965                 CHECK(prec - len);
00966                 if (dots) PUSH("..", 2);
00967                 if (!sign && valsign < 0) {
00968                     char c = sign_bits(base, p);
00969                     while (len < prec--) {
00970                         buf[blen++] = c;
00971                     }
00972                 }
00973                 else if ((flags & (FMINUS|FPREC)) != FMINUS) {
00974                     while (len < prec--) {
00975                         buf[blen++] = '0';
00976                     }
00977                 }
00978                 PUSH(s, len);
00979                 RB_GC_GUARD(tmp);
00980                 CHECK(width);
00981                 while (width-- > 0) {
00982                     buf[blen++] = ' ';
00983                 }
00984             }
00985             break;
00986 
00987           case 'f':
00988           case 'g':
00989           case 'G':
00990           case 'e':
00991           case 'E':
00992           case 'a':
00993           case 'A':
00994             {
00995                 VALUE val = GETARG();
00996                 double fval;
00997                 int i, need = 6;
00998                 char fbuf[32];
00999 
01000                 fval = RFLOAT_VALUE(rb_Float(val));
01001                 if (isnan(fval) || isinf(fval)) {
01002                     const char *expr;
01003 
01004                     if (isnan(fval)) {
01005                         expr = "NaN";
01006                     }
01007                     else {
01008                         expr = "Inf";
01009                     }
01010                     need = (int)strlen(expr);
01011                     if ((!isnan(fval) && fval < 0.0) || (flags & FPLUS))
01012                         need++;
01013                     if ((flags & FWIDTH) && need < width)
01014                         need = width;
01015 
01016                     CHECK(need + 1);
01017                     snprintf(&buf[blen], need + 1, "%*s", need, "");
01018                     if (flags & FMINUS) {
01019                         if (!isnan(fval) && fval < 0.0)
01020                             buf[blen++] = '-';
01021                         else if (flags & FPLUS)
01022                             buf[blen++] = '+';
01023                         else if (flags & FSPACE)
01024                             blen++;
01025                         memcpy(&buf[blen], expr, strlen(expr));
01026                     }
01027                     else {
01028                         if (!isnan(fval) && fval < 0.0)
01029                             buf[blen + need - strlen(expr) - 1] = '-';
01030                         else if (flags & FPLUS)
01031                             buf[blen + need - strlen(expr) - 1] = '+';
01032                         else if ((flags & FSPACE) && need > width)
01033                             blen++;
01034                         memcpy(&buf[blen + need - strlen(expr)], expr,
01035                                strlen(expr));
01036                     }
01037                     blen += strlen(&buf[blen]);
01038                     break;
01039                 }
01040 
01041                 fmt_setup(fbuf, sizeof(fbuf), *p, flags, width, prec);
01042                 need = 0;
01043                 if (*p != 'e' && *p != 'E') {
01044                     i = INT_MIN;
01045                     frexp(fval, &i);
01046                     if (i > 0)
01047                         need = BIT_DIGITS(i);
01048                 }
01049                 need += (flags&FPREC) ? prec : 6;
01050                 if ((flags&FWIDTH) && need < width)
01051                     need = width;
01052                 need += 20;
01053 
01054                 CHECK(need);
01055                 snprintf(&buf[blen], need, fbuf, fval);
01056                 blen += strlen(&buf[blen]);
01057             }
01058             break;
01059         }
01060         flags = FNONE;
01061     }
01062 
01063   sprint_exit:
01064     RB_GC_GUARD(fmt);
01065     /* XXX - We cannot validate the number of arguments if (digit)$ style used.
01066      */
01067     if (posarg >= 0 && nextarg < argc) {
01068         const char *mesg = "too many arguments for format string";
01069         if (RTEST(ruby_debug)) rb_raise(rb_eArgError, "%s", mesg);
01070         if (RTEST(ruby_verbose)) rb_warn("%s", mesg);
01071     }
01072     rb_str_resize(result, blen);
01073 
01074     if (tainted) OBJ_TAINT(result);
01075     return result;
01076 }
01077 
01078 static void
01079 fmt_setup(char *buf, size_t size, int c, int flags, int width, int prec)
01080 {
01081     char *end = buf + size;
01082     *buf++ = '%';
01083     if (flags & FSHARP) *buf++ = '#';
01084     if (flags & FPLUS)  *buf++ = '+';
01085     if (flags & FMINUS) *buf++ = '-';
01086     if (flags & FZERO)  *buf++ = '0';
01087     if (flags & FSPACE) *buf++ = ' ';
01088 
01089     if (flags & FWIDTH) {
01090         snprintf(buf, end - buf, "%d", width);
01091         buf += strlen(buf);
01092     }
01093 
01094     if (flags & FPREC) {
01095         snprintf(buf, end - buf, ".%d", prec);
01096         buf += strlen(buf);
01097     }
01098 
01099     *buf++ = c;
01100     *buf = '\0';
01101 }
01102 
01103 #undef FILE
01104 #define FILE rb_printf_buffer
01105 #define __sbuf rb_printf_sbuf
01106 #define __sFILE rb_printf_sfile
01107 #undef feof
01108 #undef ferror
01109 #undef clearerr
01110 #undef fileno
01111 #if SIZEOF_LONG < SIZEOF_VOIDP
01112 # if  SIZEOF_LONG_LONG == SIZEOF_VOIDP
01113 #  define _HAVE_SANE_QUAD_
01114 #  define _HAVE_LLP64_
01115 #  define quad_t LONG_LONG
01116 #  define u_quad_t unsigned LONG_LONG
01117 # endif
01118 #elif SIZEOF_LONG != SIZEOF_LONG_LONG && SIZEOF_LONG_LONG == 8
01119 # define _HAVE_SANE_QUAD_
01120 # define quad_t LONG_LONG
01121 # define u_quad_t unsigned LONG_LONG
01122 #endif
01123 #define FLOATING_POINT 1
01124 #define BSD__dtoa ruby_dtoa
01125 #define BSD__hdtoa ruby_hdtoa
01126 #include "vsnprintf.c"
01127 
01128 typedef struct {
01129     rb_printf_buffer base;
01130     volatile VALUE value;
01131 } rb_printf_buffer_extra;
01132 
01133 static int
01134 ruby__sfvwrite(register rb_printf_buffer *fp, register struct __suio *uio)
01135 {
01136     struct __siov *iov;
01137     VALUE result = (VALUE)fp->_bf._base;
01138     char *buf = (char*)fp->_p;
01139     size_t len, n;
01140     size_t blen = buf - RSTRING_PTR(result), bsiz = fp->_w;
01141 
01142     if (RBASIC(result)->klass) {
01143         rb_raise(rb_eRuntimeError, "rb_vsprintf reentered");
01144     }
01145     if ((len = uio->uio_resid) == 0)
01146         return 0;
01147     CHECK(len);
01148     buf += blen;
01149     fp->_w = bsiz;
01150     for (iov = uio->uio_iov; len > 0; ++iov) {
01151         MEMCPY(buf, iov->iov_base, char, n = iov->iov_len);
01152         buf += n;
01153         len -= n;
01154     }
01155     fp->_p = (unsigned char *)buf;
01156     rb_str_set_len(result, buf - RSTRING_PTR(result));
01157     return 0;
01158 }
01159 
01160 static char *
01161 ruby__sfvextra(rb_printf_buffer *fp, size_t valsize, void *valp, long *sz, int sign)
01162 {
01163     VALUE value, result = (VALUE)fp->_bf._base;
01164     rb_encoding *enc;
01165     char *cp;
01166 
01167     if (valsize != sizeof(VALUE)) return 0;
01168     value = *(VALUE *)valp;
01169     if (RBASIC(result)->klass) {
01170         rb_raise(rb_eRuntimeError, "rb_vsprintf reentered");
01171     }
01172     if (sign == '+') {
01173         value = rb_inspect(value);
01174     }
01175     else {
01176         value = rb_obj_as_string(value);
01177         if (sign == ' ') value = QUOTE(value);
01178     }
01179     enc = rb_enc_compatible(result, value);
01180     if (enc) {
01181         rb_enc_associate(result, enc);
01182     }
01183     else {
01184         enc = rb_enc_get(result);
01185         value = rb_str_conv_enc_opts(value, rb_enc_get(value), enc,
01186                                      ECONV_UNDEF_REPLACE|ECONV_INVALID_REPLACE,
01187                                      Qnil);
01188         *(volatile VALUE *)valp = value;
01189     }
01190     StringValueCStr(value);
01191     RSTRING_GETMEM(value, cp, *sz);
01192     ((rb_printf_buffer_extra *)fp)->value = value;
01193     OBJ_INFECT(result, value);
01194     return cp;
01195 }
01196 
01197 VALUE
01198 rb_enc_vsprintf(rb_encoding *enc, const char *fmt, va_list ap)
01199 {
01200     rb_printf_buffer_extra buffer;
01201 #define f buffer.base
01202     VALUE result;
01203 
01204     f._flags = __SWR | __SSTR;
01205     f._bf._size = 0;
01206     f._w = 120;
01207     result = rb_str_buf_new(f._w);
01208     if (enc) {
01209         if (rb_enc_mbminlen(enc) > 1) {
01210             /* the implementation deeply depends on plain char */
01211             rb_raise(rb_eArgError, "cannot construct wchar_t based encoding string: %s",
01212                      rb_enc_name(enc));
01213         }
01214         rb_enc_associate(result, enc);
01215     }
01216     f._bf._base = (unsigned char *)result;
01217     f._p = (unsigned char *)RSTRING_PTR(result);
01218     RBASIC_CLEAR_CLASS(result);
01219     f.vwrite = ruby__sfvwrite;
01220     f.vextra = ruby__sfvextra;
01221     buffer.value = 0;
01222     BSD_vfprintf(&f, fmt, ap);
01223     RBASIC_SET_CLASS_RAW(result, rb_cString);
01224     rb_str_resize(result, (char *)f._p - RSTRING_PTR(result));
01225 #undef f
01226 
01227     return result;
01228 }
01229 
01230 VALUE
01231 rb_enc_sprintf(rb_encoding *enc, const char *format, ...)
01232 {
01233     VALUE result;
01234     va_list ap;
01235 
01236     va_start(ap, format);
01237     result = rb_enc_vsprintf(enc, format, ap);
01238     va_end(ap);
01239 
01240     return result;
01241 }
01242 
01243 VALUE
01244 rb_vsprintf(const char *fmt, va_list ap)
01245 {
01246     return rb_enc_vsprintf(NULL, fmt, ap);
01247 }
01248 
01249 VALUE
01250 rb_sprintf(const char *format, ...)
01251 {
01252     VALUE result;
01253     va_list ap;
01254 
01255     va_start(ap, format);
01256     result = rb_vsprintf(format, ap);
01257     va_end(ap);
01258 
01259     return result;
01260 }
01261 
01262 VALUE
01263 rb_str_vcatf(VALUE str, const char *fmt, va_list ap)
01264 {
01265     rb_printf_buffer_extra buffer;
01266 #define f buffer.base
01267     VALUE klass;
01268 
01269     StringValue(str);
01270     rb_str_modify(str);
01271     f._flags = __SWR | __SSTR;
01272     f._bf._size = 0;
01273     f._w = rb_str_capacity(str);
01274     f._bf._base = (unsigned char *)str;
01275     f._p = (unsigned char *)RSTRING_END(str);
01276     klass = RBASIC(str)->klass;
01277     RBASIC_CLEAR_CLASS(str);
01278     f.vwrite = ruby__sfvwrite;
01279     f.vextra = ruby__sfvextra;
01280     buffer.value = 0;
01281     BSD_vfprintf(&f, fmt, ap);
01282     RBASIC_SET_CLASS_RAW(str, klass);
01283     rb_str_resize(str, (char *)f._p - RSTRING_PTR(str));
01284 #undef f
01285 
01286     return str;
01287 }
01288 
01289 VALUE
01290 rb_str_catf(VALUE str, const char *format, ...)
01291 {
01292     va_list ap;
01293 
01294     va_start(ap, format);
01295     str = rb_str_vcatf(str, format, ap);
01296     va_end(ap);
01297 
01298     return str;
01299 }
01300 

Generated on 19 Jul 2016 for Ruby by  doxygen 1.4.7