00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015 #include "ruby.h"
00016 #include <ctype.h>
00017 #include <math.h>
00018
00019 #define BIT_DIGITS(N) (((N)*146)/485 + 1)
00020
00021 static void fmt_setup (char*,int,int,int,int);
00022
00023 static char*
00024 remove_sign_bits(str, base)
00025 char *str;
00026 int base;
00027 {
00028 char *s, *t;
00029
00030 s = t = str;
00031
00032 if (base == 16) {
00033 while (*t == 'f') {
00034 t++;
00035 }
00036 }
00037 else if (base == 8) {
00038 if (*t == '3') t++;
00039 while (*t == '7') {
00040 t++;
00041 }
00042 }
00043 else if (base == 2) {
00044 while (*t == '1') {
00045 t++;
00046 }
00047 }
00048 if (t > s) {
00049 while (*t) *s++ = *t++;
00050 *s = '\0';
00051 }
00052
00053 return str;
00054 }
00055
00056 static char
00057 sign_bits(base, p)
00058 int base;
00059 const char *p;
00060 {
00061 char c = '.';
00062
00063 switch (base) {
00064 case 16:
00065 if (*p == 'X') c = 'F';
00066 else c = 'f';
00067 break;
00068 case 8:
00069 c = '7'; break;
00070 case 2:
00071 c = '1'; break;
00072 }
00073 return c;
00074 }
00075
00076 #define FNONE 0
00077 #define FSHARP 1
00078 #define FMINUS 2
00079 #define FPLUS 4
00080 #define FZERO 8
00081 #define FSPACE 16
00082 #define FWIDTH 32
00083 #define FPREC 64
00084
00085 #define CHECK(l) do {\
00086 while (blen + (l) >= bsiz) {\
00087 bsiz*=2;\
00088 }\
00089 rb_str_resize(result, bsiz);\
00090 buf = RSTRING(result)->ptr;\
00091 } while (0)
00092
00093 #define PUSH(s, l) do { \
00094 CHECK(l);\
00095 memcpy(&buf[blen], s, l);\
00096 blen += (l);\
00097 } while (0)
00098
00099 #define GETARG() (nextvalue != Qundef ? nextvalue : \
00100 posarg < 0 ? \
00101 (rb_raise(rb_eArgError, "unnumbered(%d) mixed with numbered", nextarg), 0) : \
00102 (posarg = nextarg++, GETNTHARG(posarg)))
00103
00104 #define GETPOSARG(n) (posarg > 0 ? \
00105 (rb_raise(rb_eArgError, "numbered(%d) after unnumbered(%d)", n, posarg), 0) : \
00106 ((n < 1) ? (rb_raise(rb_eArgError, "invalid index - %d$", n), 0) : \
00107 (posarg = -1, GETNTHARG(n))))
00108
00109 #define GETNTHARG(nth) \
00110 ((nth >= argc) ? (rb_raise(rb_eArgError, "too few arguments"), 0) : argv[nth])
00111
00112 #define GETASTER(val) do { \
00113 t = p++; \
00114 n = 0; \
00115 for (; p < end && ISDIGIT(*p); p++) { \
00116 int next_n = 10 * n + (*p - '0'); \
00117 if (next_n / 10 != n) {\
00118 rb_raise(rb_eArgError, #val " too big"); \
00119 } \
00120 n = next_n; \
00121 } \
00122 if (p >= end) { \
00123 rb_raise(rb_eArgError, "malformed format string - %%*[0-9]"); \
00124 } \
00125 if (*p == '$') { \
00126 tmp = GETPOSARG(n); \
00127 } \
00128 else { \
00129 tmp = GETARG(); \
00130 p = t; \
00131 } \
00132 val = NUM2INT(tmp); \
00133 } while (0)
00134
00135
00136
00137
00138
00139
00140
00141
00142
00143
00144
00145
00146
00147
00148
00149
00150
00151
00152
00153
00154
00155
00156
00157
00158
00159
00160
00161
00162
00163
00164
00165
00166
00167
00168
00169
00170
00171
00172
00173
00174
00175
00176
00177
00178
00179
00180
00181
00182
00183
00184
00185
00186
00187
00188
00189
00190
00191
00192
00193
00194
00195
00196
00197
00198
00199
00200
00201
00202
00203
00204
00205
00206
00207
00208
00209
00210
00211
00212
00213
00214
00215
00216
00217
00218
00219
00220
00221
00222
00223
00224
00225
00226
00227
00228
00229
00230
00231
00232
00233
00234 VALUE
00235 rb_f_sprintf(argc, argv)
00236 int argc;
00237 VALUE *argv;
00238 {
00239 VALUE fmt;
00240 const char *p, *end;
00241 char *buf;
00242 int blen, bsiz;
00243 VALUE result;
00244
00245 int width, prec, flags = FNONE;
00246 int nextarg = 1;
00247 int posarg = 0;
00248 int tainted = 0;
00249 VALUE nextvalue;
00250 VALUE tmp;
00251 VALUE str;
00252
00253 fmt = GETNTHARG(0);
00254 if (OBJ_TAINTED(fmt)) tainted = 1;
00255 StringValue(fmt);
00256 fmt = rb_str_new4(fmt);
00257 p = RSTRING(fmt)->ptr;
00258 end = p + RSTRING(fmt)->len;
00259 blen = 0;
00260 bsiz = 120;
00261 result = rb_str_buf_new(bsiz);
00262 buf = RSTRING(result)->ptr;
00263
00264 for (; p < end; p++) {
00265 const char *t;
00266 int n;
00267
00268 for (t = p; t < end && *t != '%'; t++) ;
00269 PUSH(p, t - p);
00270 if (t >= end) {
00271
00272 goto sprint_exit;
00273 }
00274 p = t + 1;
00275
00276 width = prec = -1;
00277 nextvalue = Qundef;
00278 retry:
00279 switch (*p) {
00280 default:
00281 if (ISPRINT(*p))
00282 rb_raise(rb_eArgError, "malformed format string - %%%c", *p);
00283 else
00284 rb_raise(rb_eArgError, "malformed format string");
00285 break;
00286
00287 case ' ':
00288 flags |= FSPACE;
00289 p++;
00290 goto retry;
00291
00292 case '#':
00293 flags |= FSHARP;
00294 p++;
00295 goto retry;
00296
00297 case '+':
00298 flags |= FPLUS;
00299 p++;
00300 goto retry;
00301
00302 case '-':
00303 flags |= FMINUS;
00304 p++;
00305 goto retry;
00306
00307 case '0':
00308 flags |= FZERO;
00309 p++;
00310 goto retry;
00311
00312 case '1': case '2': case '3': case '4':
00313 case '5': case '6': case '7': case '8': case '9':
00314 n = 0;
00315 for (; p < end && ISDIGIT(*p); p++) {
00316 int next_n = 10 * n + (*p - '0');
00317 if (next_n / 10 != n) {
00318 rb_raise(rb_eArgError, "width too big");
00319 }
00320 n = 10 * n + (*p - '0');
00321 }
00322 if (p >= end) {
00323 rb_raise(rb_eArgError, "malformed format string - %%[0-9]");
00324 }
00325 if (*p == '$') {
00326 if (nextvalue != Qundef) {
00327 rb_raise(rb_eArgError, "value given twice - %d$", n);
00328 }
00329 nextvalue = GETPOSARG(n);
00330 p++;
00331 goto retry;
00332 }
00333 width = n;
00334 flags |= FWIDTH;
00335 goto retry;
00336
00337 case '*':
00338 if (flags & FWIDTH) {
00339 rb_raise(rb_eArgError, "width given twice");
00340 }
00341
00342 flags |= FWIDTH;
00343 GETASTER(width);
00344 if (width < 0) {
00345 flags |= FMINUS;
00346 width = -width;
00347 }
00348 p++;
00349 goto retry;
00350
00351 case '.':
00352 if (flags & FPREC) {
00353 rb_raise(rb_eArgError, "precision given twice");
00354 }
00355 flags |= FPREC;
00356
00357 prec = 0;
00358 p++;
00359 if (*p == '*') {
00360 GETASTER(prec);
00361 if (prec < 0) {
00362 flags &= ~FPREC;
00363 }
00364 p++;
00365 goto retry;
00366 }
00367
00368 for (; p < end && ISDIGIT(*p); p++) {
00369 prec = 10 * prec + (*p - '0');
00370 }
00371 if (p >= end) {
00372 rb_raise(rb_eArgError, "malformed format string - %%.[0-9]");
00373 }
00374 goto retry;
00375
00376 case '\n':
00377 p--;
00378 case '\0':
00379 case '%':
00380 if (flags != FNONE) {
00381 rb_raise(rb_eArgError, "illegal format character - %%");
00382 }
00383 PUSH("%", 1);
00384 break;
00385
00386 case 'c':
00387 {
00388 VALUE val = GETARG();
00389 char c;
00390
00391 if (!(flags & FMINUS))
00392 while (--width > 0)
00393 PUSH(" ", 1);
00394 c = NUM2INT(val) & 0xff;
00395 PUSH(&c, 1);
00396 while (--width > 0)
00397 PUSH(" ", 1);
00398 }
00399 break;
00400
00401 case 's':
00402 case 'p':
00403 {
00404 VALUE arg = GETARG();
00405 long len;
00406
00407 if (*p == 'p') arg = rb_inspect(arg);
00408 str = rb_obj_as_string(arg);
00409 if (OBJ_TAINTED(str)) tainted = 1;
00410 len = RSTRING(str)->len;
00411 if (flags&FPREC) {
00412 if (prec < len) {
00413 len = prec;
00414 }
00415 }
00416 if (flags&FWIDTH) {
00417 if (width > len) {
00418 CHECK(width);
00419 width -= len;
00420 if (!(flags&FMINUS)) {
00421 while (width--) {
00422 buf[blen++] = ' ';
00423 }
00424 }
00425 memcpy(&buf[blen], RSTRING(str)->ptr, len);
00426 blen += len;
00427 if (flags&FMINUS) {
00428 while (width--) {
00429 buf[blen++] = ' ';
00430 }
00431 }
00432 break;
00433 }
00434 }
00435 PUSH(RSTRING(str)->ptr, len);
00436 }
00437 break;
00438
00439 case 'd':
00440 case 'i':
00441 case 'o':
00442 case 'x':
00443 case 'X':
00444 case 'b':
00445 case 'B':
00446 case 'u':
00447 {
00448 volatile VALUE val = GETARG();
00449 char fbuf[32], nbuf[64], *s, *t;
00450 char *prefix = 0;
00451 int sign = 0;
00452 char sc = 0;
00453 long v = 0;
00454 int base, bignum = 0;
00455 int len, pos;
00456 VALUE tmp;
00457 volatile VALUE tmp1;
00458
00459 switch (*p) {
00460 case 'd':
00461 case 'i':
00462 sign = 1; break;
00463 case 'o':
00464 case 'x':
00465 case 'X':
00466 case 'b':
00467 case 'B':
00468 case 'u':
00469 default:
00470 if (flags&(FPLUS|FSPACE)) sign = 1;
00471 break;
00472 }
00473 if (flags & FSHARP) {
00474 switch (*p) {
00475 case 'o':
00476 prefix = "0"; break;
00477 case 'x':
00478 prefix = "0x"; break;
00479 case 'X':
00480 prefix = "0X"; break;
00481 case 'b':
00482 prefix = "0b"; break;
00483 case 'B':
00484 prefix = "0B"; break;
00485 }
00486 if (prefix) {
00487 width -= strlen(prefix);
00488 }
00489 }
00490
00491 bin_retry:
00492 switch (TYPE(val)) {
00493 case T_FLOAT:
00494 val = rb_dbl2big(RFLOAT(val)->value);
00495 if (FIXNUM_P(val)) goto bin_retry;
00496 bignum = 1;
00497 break;
00498 case T_STRING:
00499 val = rb_str_to_inum(val, 0, Qtrue);
00500 goto bin_retry;
00501 case T_BIGNUM:
00502 bignum = 1;
00503 break;
00504 case T_FIXNUM:
00505 v = FIX2LONG(val);
00506 break;
00507 default:
00508 val = rb_Integer(val);
00509 goto bin_retry;
00510 }
00511
00512 switch (*p) {
00513 case 'o':
00514 base = 8; break;
00515 case 'x':
00516 case 'X':
00517 base = 16; break;
00518 case 'b':
00519 case 'B':
00520 base = 2; break;
00521 case 'u':
00522 case 'd':
00523 case 'i':
00524 default:
00525 base = 10; break;
00526 }
00527 if (!bignum) {
00528 if (base == 2) {
00529 val = rb_int2big(v);
00530 goto bin_retry;
00531 }
00532 if (sign) {
00533 char c = *p;
00534 if (c == 'i') c = 'd';
00535 if (v < 0) {
00536 v = -v;
00537 sc = '-';
00538 width--;
00539 }
00540 else if (flags & FPLUS) {
00541 sc = '+';
00542 width--;
00543 }
00544 else if (flags & FSPACE) {
00545 sc = ' ';
00546 width--;
00547 }
00548 sprintf(fbuf, "%%l%c", c);
00549 sprintf(nbuf, fbuf, v);
00550 s = nbuf;
00551 goto format_integer;
00552 }
00553 s = nbuf;
00554 if (v < 0) {
00555 if (base == 10) {
00556 rb_warning("negative number for %%u specifier");
00557 }
00558 if (!(flags&(FPREC|FZERO))) {
00559 strcpy(s, "..");
00560 s += 2;
00561 }
00562 }
00563 sprintf(fbuf, "%%l%c", *p == 'X' ? 'x' : *p);
00564 sprintf(s, fbuf, v);
00565 if (v < 0) {
00566 char d = 0;
00567
00568 remove_sign_bits(s, base);
00569 switch (base) {
00570 case 16:
00571 d = 'f'; break;
00572 case 8:
00573 d = '7'; break;
00574 }
00575 if (d && *s != d) {
00576 memmove(s+1, s, strlen(s)+1);
00577 *s = d;
00578 }
00579 }
00580 s = nbuf;
00581 goto format_integer;
00582 }
00583
00584 if (sign) {
00585 tmp = rb_big2str(val, base);
00586 s = RSTRING(tmp)->ptr;
00587 if (s[0] == '-') {
00588 s++;
00589 sc = '-';
00590 width--;
00591 }
00592 else if (flags & FPLUS) {
00593 sc = '+';
00594 width--;
00595 }
00596 else if (flags & FSPACE) {
00597 sc = ' ';
00598 width--;
00599 }
00600 goto format_integer;
00601 }
00602 if (!RBIGNUM(val)->sign) {
00603 val = rb_big_clone(val);
00604 rb_big_2comp(val);
00605 }
00606 tmp1 = tmp = rb_big2str(val, base);
00607 s = RSTRING(tmp)->ptr;
00608 if (*s == '-') {
00609 if (base == 10) {
00610 rb_warning("negative number for %%u specifier");
00611 s++;
00612 }
00613 else {
00614 remove_sign_bits(++s, base);
00615 tmp = rb_str_new(0, 3+strlen(s));
00616 t = RSTRING(tmp)->ptr;
00617 if (!(flags&(FPREC|FZERO))) {
00618 strcpy(t, "..");
00619 t += 2;
00620 }
00621 switch (base) {
00622 case 16:
00623 if (s[0] != 'f') strcpy(t++, "f"); break;
00624 case 8:
00625 if (s[0] != '7') strcpy(t++, "7"); break;
00626 case 2:
00627 if (s[0] != '1') strcpy(t++, "1"); break;
00628 }
00629 strcpy(t, s);
00630 bignum = 2;
00631 }
00632 }
00633 s = RSTRING(tmp)->ptr;
00634
00635 format_integer:
00636 pos = -1;
00637 len = strlen(s);
00638
00639 if (*p == 'X') {
00640 char *pp = s;
00641 while (*pp) {
00642 *pp = toupper(*pp);
00643 pp++;
00644 }
00645 }
00646 if ((flags&(FZERO|FPREC)) == FZERO) {
00647 prec = width;
00648 width = 0;
00649 }
00650 else {
00651 if (prec < len) prec = len;
00652 width -= prec;
00653 }
00654 if (!(flags&FMINUS)) {
00655 CHECK(width);
00656 while (width-- > 0) {
00657 buf[blen++] = ' ';
00658 }
00659 }
00660 if (sc) PUSH(&sc, 1);
00661 if (prefix) {
00662 int plen = strlen(prefix);
00663 PUSH(prefix, plen);
00664 }
00665 CHECK(prec - len);
00666 if (!bignum && v < 0) {
00667 char c = sign_bits(base, p);
00668 while (len < prec--) {
00669 buf[blen++] = c;
00670 }
00671 }
00672 else {
00673 char c;
00674
00675 if (bignum && !RBIGNUM(val)->sign)
00676 c = sign_bits(base, p);
00677 else
00678 c = '0';
00679 while (len < prec--) {
00680 buf[blen++] = c;
00681 }
00682 }
00683 PUSH(s, len);
00684 CHECK(width);
00685 while (width-- > 0) {
00686 buf[blen++] = ' ';
00687 }
00688 }
00689 break;
00690
00691 case 'f':
00692 case 'g':
00693 case 'G':
00694 case 'e':
00695 case 'E':
00696 {
00697 VALUE val = GETARG();
00698 double fval;
00699 int i, need = 6;
00700 char fbuf[32];
00701
00702 fval = RFLOAT(rb_Float(val))->value;
00703 #if defined(_WIN32) && !defined(__BORLANDC__)
00704 if (isnan(fval) || isinf(fval)) {
00705 char *expr;
00706
00707 if (isnan(fval)) {
00708 expr = "NaN";
00709 }
00710 else {
00711 expr = "Inf";
00712 }
00713 need = strlen(expr);
00714 if ((!isnan(fval) && fval < 0.0) || (flags & FPLUS))
00715 need++;
00716 if ((flags & FWIDTH) && need < width)
00717 need = width;
00718
00719 CHECK(need);
00720 sprintf(&buf[blen], "%*s", need, "");
00721 if (flags & FMINUS) {
00722 if (!isnan(fval) && fval < 0.0)
00723 buf[blen++] = '-';
00724 else if (flags & FPLUS)
00725 buf[blen++] = '+';
00726 else if (flags & FSPACE)
00727 blen++;
00728 strncpy(&buf[blen], expr, strlen(expr));
00729 }
00730 else if (flags & FZERO) {
00731 if (!isnan(fval) && fval < 0.0) {
00732 buf[blen++] = '-';
00733 need--;
00734 }
00735 else if (flags & FPLUS) {
00736 buf[blen++] = '+';
00737 need--;
00738 }
00739 else if (flags & FSPACE) {
00740 blen++;
00741 need--;
00742 }
00743 while (need-- - strlen(expr) > 0) {
00744 buf[blen++] = '0';
00745 }
00746 strncpy(&buf[blen], expr, strlen(expr));
00747 }
00748 else {
00749 if (!isnan(fval) && fval < 0.0)
00750 buf[blen + need - strlen(expr) - 1] = '-';
00751 else if (flags & FPLUS)
00752 buf[blen + need - strlen(expr) - 1] = '+';
00753 strncpy(&buf[blen + need - strlen(expr)], expr,
00754 strlen(expr));
00755 }
00756 blen += strlen(&buf[blen]);
00757 break;
00758 }
00759 #endif
00760 fmt_setup(fbuf, *p, flags, width, prec);
00761 need = 0;
00762 if (*p != 'e' && *p != 'E') {
00763 i = INT_MIN;
00764 frexp(fval, &i);
00765 if (i > 0)
00766 need = BIT_DIGITS(i);
00767 }
00768 need += (flags&FPREC) ? prec : 6;
00769 if ((flags&FWIDTH) && need < width)
00770 need = width;
00771 need += 20;
00772
00773 CHECK(need);
00774 sprintf(&buf[blen], fbuf, fval);
00775 blen += strlen(&buf[blen]);
00776 }
00777 break;
00778 }
00779 flags = FNONE;
00780 }
00781
00782 sprint_exit:
00783
00784
00785 if (posarg >= 0 && nextarg < argc) {
00786 const char *mesg = "too many arguments for format string";
00787 if (RTEST(ruby_debug)) rb_raise(rb_eArgError, mesg);
00788 if (RTEST(ruby_verbose)) rb_warn(mesg);
00789 }
00790 rb_str_resize(result, blen);
00791
00792 if (tainted) OBJ_TAINT(result);
00793 return result;
00794 }
00795
00796 static void
00797 fmt_setup(buf, c, flags, width, prec)
00798 char *buf;
00799 int c;
00800 int flags, width, prec;
00801 {
00802 *buf++ = '%';
00803 if (flags & FSHARP) *buf++ = '#';
00804 if (flags & FPLUS) *buf++ = '+';
00805 if (flags & FMINUS) *buf++ = '-';
00806 if (flags & FZERO) *buf++ = '0';
00807 if (flags & FSPACE) *buf++ = ' ';
00808
00809 if (flags & FWIDTH) {
00810 sprintf(buf, "%d", width);
00811 buf += strlen(buf);
00812 }
00813
00814 if (flags & FPREC) {
00815 sprintf(buf, ".%d", prec);
00816 buf += strlen(buf);
00817 }
00818
00819 *buf++ = c;
00820 *buf = '\0';
00821 }
00822