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 n = 10 * n + (*p - '0'); \
00117 } \
00118 if (p >= end) { \
00119 rb_raise(rb_eArgError, "malformed format string - %%*[0-9]"); \
00120 } \
00121 if (*p == '$') { \
00122 tmp = GETPOSARG(n); \
00123 } \
00124 else { \
00125 tmp = GETARG(); \
00126 p = t; \
00127 } \
00128 val = NUM2INT(tmp); \
00129 } while (0)
00130
00131
00132
00133
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 VALUE
00231 rb_f_sprintf(argc, argv)
00232 int argc;
00233 VALUE *argv;
00234 {
00235 VALUE fmt;
00236 const char *p, *end;
00237 char *buf;
00238 int blen, bsiz;
00239 VALUE result;
00240
00241 int width, prec, flags = FNONE;
00242 int nextarg = 1;
00243 int posarg = 0;
00244 int tainted = 0;
00245 VALUE nextvalue;
00246 VALUE tmp;
00247 VALUE str;
00248
00249 fmt = GETNTHARG(0);
00250 if (OBJ_TAINTED(fmt)) tainted = 1;
00251 StringValue(fmt);
00252 fmt = rb_str_new4(fmt);
00253 p = RSTRING(fmt)->ptr;
00254 end = p + RSTRING(fmt)->len;
00255 blen = 0;
00256 bsiz = 120;
00257 result = rb_str_buf_new(bsiz);
00258 buf = RSTRING(result)->ptr;
00259
00260 for (; p < end; p++) {
00261 const char *t;
00262 int n;
00263
00264 for (t = p; t < end && *t != '%'; t++) ;
00265 PUSH(p, t - p);
00266 if (t >= end) {
00267
00268 goto sprint_exit;
00269 }
00270 p = t + 1;
00271
00272 width = prec = -1;
00273 nextvalue = Qundef;
00274 retry:
00275 switch (*p) {
00276 default:
00277 if (ISPRINT(*p))
00278 rb_raise(rb_eArgError, "malformed format string - %%%c", *p);
00279 else
00280 rb_raise(rb_eArgError, "malformed format string");
00281 break;
00282
00283 case ' ':
00284 flags |= FSPACE;
00285 p++;
00286 goto retry;
00287
00288 case '#':
00289 flags |= FSHARP;
00290 p++;
00291 goto retry;
00292
00293 case '+':
00294 flags |= FPLUS;
00295 p++;
00296 goto retry;
00297
00298 case '-':
00299 flags |= FMINUS;
00300 p++;
00301 goto retry;
00302
00303 case '0':
00304 flags |= FZERO;
00305 p++;
00306 goto retry;
00307
00308 case '1': case '2': case '3': case '4':
00309 case '5': case '6': case '7': case '8': case '9':
00310 n = 0;
00311 for (; p < end && ISDIGIT(*p); p++) {
00312 n = 10 * n + (*p - '0');
00313 }
00314 if (p >= end) {
00315 rb_raise(rb_eArgError, "malformed format string - %%[0-9]");
00316 }
00317 if (*p == '$') {
00318 if (nextvalue != Qundef) {
00319 rb_raise(rb_eArgError, "value given twice - %d$", n);
00320 }
00321 nextvalue = GETPOSARG(n);
00322 p++;
00323 goto retry;
00324 }
00325 width = n;
00326 flags |= FWIDTH;
00327 goto retry;
00328
00329 case '*':
00330 if (flags & FWIDTH) {
00331 rb_raise(rb_eArgError, "width given twice");
00332 }
00333
00334 flags |= FWIDTH;
00335 GETASTER(width);
00336 if (width < 0) {
00337 flags |= FMINUS;
00338 width = -width;
00339 }
00340 p++;
00341 goto retry;
00342
00343 case '.':
00344 if (flags & FPREC) {
00345 rb_raise(rb_eArgError, "precision given twice");
00346 }
00347 flags |= FPREC;
00348
00349 prec = 0;
00350 p++;
00351 if (*p == '*') {
00352 GETASTER(prec);
00353 if (prec < 0) {
00354 flags &= ~FPREC;
00355 }
00356 p++;
00357 goto retry;
00358 }
00359
00360 for (; p < end && ISDIGIT(*p); p++) {
00361 prec = 10 * prec + (*p - '0');
00362 }
00363 if (p >= end) {
00364 rb_raise(rb_eArgError, "malformed format string - %%.[0-9]");
00365 }
00366 goto retry;
00367
00368 case '\n':
00369 p--;
00370 case '\0':
00371 case '%':
00372 if (flags != FNONE) {
00373 rb_raise(rb_eArgError, "illegal format character - %%");
00374 }
00375 PUSH("%", 1);
00376 break;
00377
00378 case 'c':
00379 {
00380 VALUE val = GETARG();
00381 char c;
00382
00383 if (!(flags & FMINUS))
00384 while (--width > 0)
00385 PUSH(" ", 1);
00386 c = NUM2INT(val) & 0xff;
00387 PUSH(&c, 1);
00388 while (--width > 0)
00389 PUSH(" ", 1);
00390 }
00391 break;
00392
00393 case 's':
00394 case 'p':
00395 {
00396 VALUE arg = GETARG();
00397 long len;
00398
00399 if (*p == 'p') arg = rb_inspect(arg);
00400 str = rb_obj_as_string(arg);
00401 if (OBJ_TAINTED(str)) tainted = 1;
00402 len = RSTRING(str)->len;
00403 if (flags&FPREC) {
00404 if (prec < len) {
00405 len = prec;
00406 }
00407 }
00408 if (flags&FWIDTH) {
00409 if (width > len) {
00410 CHECK(width);
00411 width -= len;
00412 if (!(flags&FMINUS)) {
00413 while (width--) {
00414 buf[blen++] = ' ';
00415 }
00416 }
00417 memcpy(&buf[blen], RSTRING(str)->ptr, len);
00418 blen += len;
00419 if (flags&FMINUS) {
00420 while (width--) {
00421 buf[blen++] = ' ';
00422 }
00423 }
00424 break;
00425 }
00426 }
00427 PUSH(RSTRING(str)->ptr, len);
00428 }
00429 break;
00430
00431 case 'd':
00432 case 'i':
00433 case 'o':
00434 case 'x':
00435 case 'X':
00436 case 'b':
00437 case 'B':
00438 case 'u':
00439 {
00440 volatile VALUE val = GETARG();
00441 char fbuf[32], nbuf[64], *s, *t;
00442 char *prefix = 0;
00443 int sign = 0;
00444 char sc = 0;
00445 long v = 0;
00446 int base, bignum = 0;
00447 int len, pos;
00448 VALUE tmp;
00449
00450 switch (*p) {
00451 case 'd':
00452 case 'i':
00453 sign = 1; break;
00454 case 'o':
00455 case 'x':
00456 case 'X':
00457 case 'b':
00458 case 'B':
00459 case 'u':
00460 default:
00461 if (flags&(FPLUS|FSPACE)) sign = 1;
00462 break;
00463 }
00464 if (flags & FSHARP) {
00465 switch (*p) {
00466 case 'o':
00467 prefix = "0"; break;
00468 case 'x':
00469 prefix = "0x"; break;
00470 case 'X':
00471 prefix = "0X"; break;
00472 case 'b':
00473 prefix = "0b"; break;
00474 case 'B':
00475 prefix = "0B"; break;
00476 }
00477 if (prefix) {
00478 width -= strlen(prefix);
00479 }
00480 }
00481
00482 bin_retry:
00483 switch (TYPE(val)) {
00484 case T_FLOAT:
00485 val = rb_dbl2big(RFLOAT(val)->value);
00486 if (FIXNUM_P(val)) goto bin_retry;
00487 bignum = 1;
00488 break;
00489 case T_STRING:
00490 val = rb_str_to_inum(val, 0, Qtrue);
00491 goto bin_retry;
00492 case T_BIGNUM:
00493 bignum = 1;
00494 break;
00495 case T_FIXNUM:
00496 v = FIX2LONG(val);
00497 break;
00498 default:
00499 val = rb_Integer(val);
00500 goto bin_retry;
00501 }
00502
00503 switch (*p) {
00504 case 'o':
00505 base = 8; break;
00506 case 'x':
00507 case 'X':
00508 base = 16; break;
00509 case 'b':
00510 case 'B':
00511 base = 2; break;
00512 case 'u':
00513 case 'd':
00514 case 'i':
00515 default:
00516 base = 10; break;
00517 }
00518 if (!bignum) {
00519 if (base == 2) {
00520 val = rb_int2big(v);
00521 goto bin_retry;
00522 }
00523 if (sign) {
00524 char c = *p;
00525 if (c == 'i') c = 'd';
00526 if (v < 0) {
00527 v = -v;
00528 sc = '-';
00529 width--;
00530 }
00531 else if (flags & FPLUS) {
00532 sc = '+';
00533 width--;
00534 }
00535 else if (flags & FSPACE) {
00536 sc = ' ';
00537 width--;
00538 }
00539 sprintf(fbuf, "%%l%c", c);
00540 sprintf(nbuf, fbuf, v);
00541 s = nbuf;
00542 goto format_integer;
00543 }
00544 s = nbuf;
00545 if (v < 0) {
00546 if (base == 10) {
00547 rb_warning("negative number for %%u specifier");
00548 }
00549 if (!(flags&(FPREC|FZERO))) {
00550 strcpy(s, "..");
00551 s += 2;
00552 }
00553 }
00554 sprintf(fbuf, "%%l%c", *p == 'X' ? 'x' : *p);
00555 sprintf(s, fbuf, v);
00556 if (v < 0) {
00557 char d = 0;
00558
00559 remove_sign_bits(s, base);
00560 switch (base) {
00561 case 16:
00562 d = 'f'; break;
00563 case 8:
00564 d = '7'; break;
00565 }
00566 if (d && *s != d) {
00567 memmove(s+1, s, strlen(s)+1);
00568 *s = d;
00569 }
00570 }
00571 s = nbuf;
00572 goto format_integer;
00573 }
00574
00575 if (sign) {
00576 tmp = rb_big2str(val, base);
00577 s = RSTRING(tmp)->ptr;
00578 if (s[0] == '-') {
00579 s++;
00580 sc = '-';
00581 width--;
00582 }
00583 else if (flags & FPLUS) {
00584 sc = '+';
00585 width--;
00586 }
00587 else if (flags & FSPACE) {
00588 sc = ' ';
00589 width--;
00590 }
00591 goto format_integer;
00592 }
00593 if (!RBIGNUM(val)->sign) {
00594 val = rb_big_clone(val);
00595 rb_big_2comp(val);
00596 }
00597 tmp = rb_big2str(val, base);
00598 s = RSTRING(tmp)->ptr;
00599 if (*s == '-') {
00600 if (base == 10) {
00601 rb_warning("negative number for %%u specifier");
00602 s++;
00603 }
00604 else {
00605 remove_sign_bits(++s, base);
00606 tmp = rb_str_new(0, 3+strlen(s));
00607 t = RSTRING(tmp)->ptr;
00608 if (!(flags&(FPREC|FZERO))) {
00609 strcpy(t, "..");
00610 t += 2;
00611 }
00612 switch (base) {
00613 case 16:
00614 if (s[0] != 'f') strcpy(t++, "f"); break;
00615 case 8:
00616 if (s[0] != '7') strcpy(t++, "7"); break;
00617 case 2:
00618 if (s[0] != '1') strcpy(t++, "1"); break;
00619 }
00620 strcpy(t, s);
00621 bignum = 2;
00622 }
00623 }
00624 s = RSTRING(tmp)->ptr;
00625
00626 format_integer:
00627 pos = -1;
00628 len = strlen(s);
00629
00630 if (*p == 'X') {
00631 char *pp = s;
00632 while (*pp) {
00633 *pp = toupper(*pp);
00634 pp++;
00635 }
00636 }
00637 if ((flags&(FZERO|FPREC)) == FZERO) {
00638 prec = width;
00639 width = 0;
00640 }
00641 else {
00642 if (prec < len) prec = len;
00643 width -= prec;
00644 }
00645 if (!(flags&FMINUS)) {
00646 CHECK(width);
00647 while (width-- > 0) {
00648 buf[blen++] = ' ';
00649 }
00650 }
00651 if (sc) PUSH(&sc, 1);
00652 if (prefix) {
00653 int plen = strlen(prefix);
00654 PUSH(prefix, plen);
00655 }
00656 CHECK(prec - len);
00657 if (!bignum && v < 0) {
00658 char c = sign_bits(base, p);
00659 while (len < prec--) {
00660 buf[blen++] = c;
00661 }
00662 }
00663 else {
00664 char c;
00665
00666 if (bignum && !RBIGNUM(val)->sign)
00667 c = sign_bits(base, p);
00668 else
00669 c = '0';
00670 while (len < prec--) {
00671 buf[blen++] = c;
00672 }
00673 }
00674 PUSH(s, len);
00675 CHECK(width);
00676 while (width-- > 0) {
00677 buf[blen++] = ' ';
00678 }
00679 }
00680 break;
00681
00682 case 'f':
00683 case 'g':
00684 case 'G':
00685 case 'e':
00686 case 'E':
00687 {
00688 VALUE val = GETARG();
00689 double fval;
00690 int i, need = 6;
00691 char fbuf[32];
00692
00693 fval = RFLOAT(rb_Float(val))->value;
00694 #if defined(_WIN32) && !defined(__BORLANDC__)
00695 if (isnan(fval) || isinf(fval)) {
00696 char *expr;
00697
00698 if (isnan(fval)) {
00699 expr = "NaN";
00700 }
00701 else {
00702 expr = "Inf";
00703 }
00704 need = strlen(expr);
00705 if ((!isnan(fval) && fval < 0.0) || (flags & FPLUS))
00706 need++;
00707 if ((flags & FWIDTH) && need < width)
00708 need = width;
00709
00710 CHECK(need);
00711 sprintf(&buf[blen], "%*s", need, "");
00712 if (flags & FMINUS) {
00713 if (!isnan(fval) && fval < 0.0)
00714 buf[blen++] = '-';
00715 else if (flags & FPLUS)
00716 buf[blen++] = '+';
00717 else if (flags & FSPACE)
00718 blen++;
00719 strncpy(&buf[blen], expr, strlen(expr));
00720 }
00721 else if (flags & FZERO) {
00722 if (!isnan(fval) && fval < 0.0) {
00723 buf[blen++] = '-';
00724 need--;
00725 }
00726 else if (flags & FPLUS) {
00727 buf[blen++] = '+';
00728 need--;
00729 }
00730 else if (flags & FSPACE) {
00731 blen++;
00732 need--;
00733 }
00734 while (need-- - strlen(expr) > 0) {
00735 buf[blen++] = '0';
00736 }
00737 strncpy(&buf[blen], expr, strlen(expr));
00738 }
00739 else {
00740 if (!isnan(fval) && fval < 0.0)
00741 buf[blen + need - strlen(expr) - 1] = '-';
00742 else if (flags & FPLUS)
00743 buf[blen + need - strlen(expr) - 1] = '+';
00744 strncpy(&buf[blen + need - strlen(expr)], expr,
00745 strlen(expr));
00746 }
00747 blen += strlen(&buf[blen]);
00748 break;
00749 }
00750 #endif
00751 fmt_setup(fbuf, *p, flags, width, prec);
00752 need = 0;
00753 if (*p != 'e' && *p != 'E') {
00754 i = INT_MIN;
00755 frexp(fval, &i);
00756 if (i > 0)
00757 need = BIT_DIGITS(i);
00758 }
00759 need += (flags&FPREC) ? prec : 6;
00760 if ((flags&FWIDTH) && need < width)
00761 need = width;
00762 need += 20;
00763
00764 CHECK(need);
00765 sprintf(&buf[blen], fbuf, fval);
00766 blen += strlen(&buf[blen]);
00767 }
00768 break;
00769 }
00770 flags = FNONE;
00771 }
00772
00773 sprint_exit:
00774
00775
00776
00777 if (RTEST(ruby_verbose) && posarg >= 0 && nextarg < argc) {
00778 rb_raise(rb_eArgError, "too many arguments for format string");
00779 }
00780 rb_str_resize(result, blen);
00781
00782 if (tainted) OBJ_TAINT(result);
00783 return result;
00784 }
00785
00786 static void
00787 fmt_setup(buf, c, flags, width, prec)
00788 char *buf;
00789 int c;
00790 int flags, width, prec;
00791 {
00792 *buf++ = '%';
00793 if (flags & FSHARP) *buf++ = '#';
00794 if (flags & FPLUS) *buf++ = '+';
00795 if (flags & FMINUS) *buf++ = '-';
00796 if (flags & FZERO) *buf++ = '0';
00797 if (flags & FSPACE) *buf++ = ' ';
00798
00799 if (flags & FWIDTH) {
00800 sprintf(buf, "%d", width);
00801 buf += strlen(buf);
00802 }
00803
00804 if (flags & FPREC) {
00805 sprintf(buf, ".%d", prec);
00806 buf += strlen(buf);
00807 }
00808
00809 *buf++ = c;
00810 *buf = '\0';
00811 }
00812