mbed I/F binding for mruby

Dependents:   mruby_mbed_web mirb_mbed

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers sprintf.c Source File

sprintf.c

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