Ruby 1.9.2p136(2010-12-25revision30365)

- Main Page
- Modules
- Data Structures
- Files

- File List
- Globals

# sprintf.c

Go to the documentation of this file.

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