Main Page   Modules   Alphabetical List   Data Structures   File List   Data Fields   Globals  

sprintf.c

Go to the documentation of this file.
00001 /**********************************************************************
00002 
00003   sprintf.c -
00004 
00005   $Author: matz $
00006   $Date: 2004/12/08 06:22:51 $
00007   created at: Fri Oct 15 10:39:26 JST 1993
00008 
00009   Copyright (C) 1993-2003 Yukihiro Matsumoto
00010   Copyright (C) 2000  Network Applied Communication Laboratory, Inc.
00011   Copyright (C) 2000  Information-technology Promotion Agency, Japan
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)  /* log2(10) =~ 146/485 */
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  *  call-seq:
00134  *     format(format_string [, arguments...] )   => string
00135  *     sprintf(format_string [, arguments...] )  => string
00136  *  
00137  *  Returns the string resulting from applying <i>format_string</i> to
00138  *  any additional arguments. Within the format string, any characters
00139  *  other than format sequences are copied to the result. A format
00140  *  sequence consists of a percent sign, followed by optional flags,
00141  *  width, and precision indicators, then terminated with a field type
00142  *  character. The field type controls how the corresponding
00143  *  <code>sprintf</code> argument is to be interpreted, while the flags
00144  *  modify that interpretation. The field type characters are listed
00145  *  in the table at the end of this section. The flag characters are:
00146  *
00147  *    Flag     | Applies to   | Meaning
00148  *    ---------+--------------+-----------------------------------------
00149  *    space    | bdeEfgGioxXu | Leave a space at the start of 
00150  *             |              | positive numbers.
00151  *    ---------+--------------+-----------------------------------------
00152  *    (digit)$ | all          | Specifies the absolute argument number
00153  *             |              | for this field. Absolute and relative
00154  *             |              | argument numbers cannot be mixed in a
00155  *             |              | sprintf string.
00156  *    ---------+--------------+-----------------------------------------
00157  *     #       | beEfgGoxX    | Use an alternative format. For the
00158  *             |              | conversions `o', `x', `X', and `b', 
00159  *             |              | prefix the result with ``0'', ``0x'', ``0X'',
00160  *             |              |  and ``0b'', respectively. For `e',
00161  *             |              | `E', `f', `g', and 'G', force a decimal
00162  *             |              | point to be added, even if no digits follow.
00163  *             |              | For `g' and 'G', do not remove trailing zeros.
00164  *    ---------+--------------+-----------------------------------------
00165  *    +        | bdeEfgGioxXu | Add a leading plus sign to positive numbers.
00166  *    ---------+--------------+-----------------------------------------
00167  *    -        | all          | Left-justify the result of this conversion.
00168  *    ---------+--------------+-----------------------------------------
00169  *    0 (zero) | all          | Pad with zeros, not spaces.
00170  *    ---------+--------------+-----------------------------------------
00171  *    *        | all          | Use the next argument as the field width. 
00172  *             |              | If negative, left-justify the result. If the
00173  *             |              | asterisk is followed by a number and a dollar 
00174  *             |              | sign, use the indicated argument as the width.
00175  *
00176  *     
00177  *  The field width is an optional integer, followed optionally by a
00178  *  period and a precision. The width specifies the minimum number of
00179  *  characters that will be written to the result for this field. For
00180  *  numeric fields, the precision controls the number of decimal places
00181  *  displayed. For string fields, the precision determines the maximum
00182  *  number of characters to be copied from the string. (Thus, the format
00183  *  sequence <code>%10.10s</code> will always contribute exactly ten
00184  *  characters to the result.)
00185  *
00186  *  The field types are:
00187  *
00188  *      Field |  Conversion
00189  *      ------+--------------------------------------------------------------
00190  *        b   | Convert argument as a binary number.
00191  *        c   | Argument is the numeric code for a single character.
00192  *        d   | Convert argument as a decimal number.
00193  *        E   | Equivalent to `e', but uses an uppercase E to indicate
00194  *            | the exponent.
00195  *        e   | Convert floating point argument into exponential notation 
00196  *            | with one digit before the decimal point. The precision
00197  *            | determines the number of fractional digits (defaulting to six).
00198  *        f   | Convert floating point argument as [-]ddd.ddd, 
00199  *            |  where the precision determines the number of digits after
00200  *            | the decimal point.
00201  *        G   | Equivalent to `g', but use an uppercase `E' in exponent form.
00202  *        g   | Convert a floating point number using exponential form
00203  *            | if the exponent is less than -4 or greater than or
00204  *            | equal to the precision, or in d.dddd form otherwise.
00205  *        i   | Identical to `d'.
00206  *        o   | Convert argument as an octal number.
00207  *        p   | The valuing of argument.inspect.
00208  *        s   | Argument is a string to be substituted. If the format
00209  *            | sequence contains a precision, at most that many characters
00210  *            | will be copied.
00211  *        u   | Treat argument as an unsigned decimal number.
00212  *        X   | Convert argument as a hexadecimal number using uppercase
00213  *            | letters. Negative numbers will be displayed with two
00214  *            | leading periods (representing an infinite string of
00215  *            | leading 'FF's.
00216  *        x   | Convert argument as a hexadecimal number.
00217  *            | Negative numbers will be displayed with two
00218  *            | leading periods (representing an infinite string of
00219  *            | leading 'ff's.
00220  *     
00221  *  Examples:
00222  *
00223  *     sprintf("%d %04x", 123, 123)               #=> "123 007b"
00224  *     sprintf("%08b '%4s'", 123, 123)            #=> "01111011 ' 123'"
00225  *     sprintf("%1$*2$s %2$d %1$s", "hello", 8)   #=> "   hello 8 hello"
00226  *     sprintf("%1$*2$s %2$d", "hello", -8)       #=> "hello    -8"
00227  *     sprintf("%+g:% g:%-g", 1.23, 1.23, 1.23)   #=> "+1.23: 1.23:1.23"
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             /* end of fmt string */
00268             goto sprint_exit;
00269         }
00270         p = t + 1;              /* skip `%' */
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) { /* ignore negative precision */
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'; /* %d and %i are identical */
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  /* defined(_WIN32) && !defined(__BORLANDC__) */
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     /* XXX - We cannot validiate the number of arguments because
00775      *       the format string may contain `n$'-style argument selector.
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 

Generated on Sat Jan 22 14:29:38 2005 for Ruby by doxygen1.2.18