mbed I/F binding for mruby
Dependents: mruby_mbed_web mirb_mbed
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
Generated on Tue Jul 12 2022 18:00:35 by 1.7.2