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