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