~ubuntu-branches/ubuntu/hardy/ruby1.8/hardy-updates

« back to all changes in this revision

Viewing changes to sprintf.c

  • Committer: Bazaar Package Importer
  • Author(s): akira yamada
  • Date: 2007-03-13 22:11:58 UTC
  • mfrom: (1.1.5 upstream)
  • Revision ID: james.westby@ubuntu.com-20070313221158-h3oql37brlaf2go2
Tags: 1.8.6-1
* new upstream version, 1.8.6.
* libruby1.8 conflicts with libopenssl-ruby1.8 (< 1.8.6) (closes: #410018)
* changed packaging style to cdbs from dbs.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/**********************************************************************
 
2
 
 
3
  sprintf.c -
 
4
 
 
5
  $Author: shyouhei $
 
6
  $Date: 2007-02-13 08:01:19 +0900 (Tue, 13 Feb 2007) $
 
7
  created at: Fri Oct 15 10:39:26 JST 1993
 
8
 
 
9
  Copyright (C) 1993-2003 Yukihiro Matsumoto
 
10
  Copyright (C) 2000  Network Applied Communication Laboratory, Inc.
 
11
  Copyright (C) 2000  Information-technology Promotion Agency, Japan
 
12
 
 
13
**********************************************************************/
 
14
 
 
15
#include "ruby.h"
 
16
#include "re.h"
 
17
#include <ctype.h>
 
18
#include <math.h>
 
19
 
 
20
#define BIT_DIGITS(N)   (((N)*146)/485 + 1)  /* log2(10) =~ 146/485 */
 
21
 
 
22
static void fmt_setup _((char*,int,int,int,int));
 
23
 
 
24
static char*
 
25
remove_sign_bits(str, base)
 
26
    char *str;
 
27
    int base;
 
28
{
 
29
    char *s, *t;
 
30
    
 
31
    s = t = str;
 
32
 
 
33
    if (base == 16) {
 
34
        while (*t == 'f') {
 
35
            t++;
 
36
        }
 
37
    }
 
38
    else if (base == 8) {
 
39
        if (*t == '3') t++;
 
40
        while (*t == '7') {
 
41
            t++;
 
42
        }
 
43
    }
 
44
    else if (base == 2) {
 
45
        while (*t == '1') {
 
46
            t++;
 
47
        }
 
48
    }
 
49
    if (t > s) {
 
50
        while (*t) *s++ = *t++;
 
51
        *s = '\0';
 
52
    }
 
53
 
 
54
    return str;
 
55
}
 
56
 
 
57
static char
 
58
sign_bits(base, p)
 
59
    int base;
 
60
    const char *p;
 
61
{
 
62
    char c = '.';
 
63
 
 
64
    switch (base) {
 
65
      case 16:
 
66
        if (*p == 'X') c = 'F';
 
67
        else c = 'f';
 
68
        break;
 
69
      case 8:
 
70
        c = '7'; break;
 
71
      case 2:
 
72
        c = '1'; break;
 
73
    }
 
74
    return c;
 
75
}
 
76
 
 
77
#define FNONE  0
 
78
#define FSHARP 1
 
79
#define FMINUS 2
 
80
#define FPLUS  4
 
81
#define FZERO  8
 
82
#define FSPACE 16
 
83
#define FWIDTH 32
 
84
#define FPREC  64
 
85
 
 
86
#define CHECK(l) do {\
 
87
    while (blen + (l) >= bsiz) {\
 
88
        bsiz*=2;\
 
89
    }\
 
90
    rb_str_resize(result, bsiz);\
 
91
    buf = RSTRING(result)->ptr;\
 
92
} while (0)
 
93
 
 
94
#define PUSH(s, l) do { \
 
95
    CHECK(l);\
 
96
    memcpy(&buf[blen], s, l);\
 
97
    blen += (l);\
 
98
} while (0)
 
99
 
 
100
#define GETARG() (nextvalue != Qundef ? nextvalue : \
 
101
    posarg < 0 ? \
 
102
    (rb_raise(rb_eArgError, "unnumbered(%d) mixed with numbered", nextarg), 0) : \
 
103
    (posarg = nextarg++, GETNTHARG(posarg)))
 
104
 
 
105
#define GETPOSARG(n) (posarg > 0 ? \
 
106
    (rb_raise(rb_eArgError, "numbered(%d) after unnumbered(%d)", n, posarg), 0) : \
 
107
    ((n < 1) ? (rb_raise(rb_eArgError, "invalid index - %d$", n), 0) : \
 
108
               (posarg = -1, GETNTHARG(n))))
 
109
 
 
110
#define GETNTHARG(nth) \
 
111
    ((nth >= argc) ? (rb_raise(rb_eArgError, "too few arguments"), 0) : argv[nth])
 
112
 
 
113
#define GETASTER(val) do { \
 
114
    t = p++; \
 
115
    n = 0; \
 
116
    for (; p < end && ISDIGIT(*p); p++) { \
 
117
        int next_n = 10 * n + (*p - '0'); \
 
118
        if (next_n / 10 != n) {\
 
119
            rb_raise(rb_eArgError, #val " too big"); \
 
120
        } \
 
121
        n = next_n; \
 
122
    } \
 
123
    if (p >= end) { \
 
124
        rb_raise(rb_eArgError, "malformed format string - %%*[0-9]"); \
 
125
    } \
 
126
    if (*p == '$') { \
 
127
        tmp = GETPOSARG(n); \
 
128
    } \
 
129
    else { \
 
130
        tmp = GETARG(); \
 
131
        p = t; \
 
132
    } \
 
133
    val = NUM2INT(tmp); \
 
134
} while (0)
 
135
 
 
136
 
 
137
/*
 
138
 *  call-seq:
 
139
 *     format(format_string [, arguments...] )   => string
 
140
 *     sprintf(format_string [, arguments...] )  => string
 
141
 *  
 
142
 *  Returns the string resulting from applying <i>format_string</i> to
 
143
 *  any additional arguments. Within the format string, any characters
 
144
 *  other than format sequences are copied to the result. A format
 
145
 *  sequence consists of a percent sign, followed by optional flags,
 
146
 *  width, and precision indicators, then terminated with a field type
 
147
 *  character. The field type controls how the corresponding
 
148
 *  <code>sprintf</code> argument is to be interpreted, while the flags
 
149
 *  modify that interpretation. The field type characters are listed
 
150
 *  in the table at the end of this section. The flag characters are:
 
151
 *
 
152
 *    Flag     | Applies to   | Meaning
 
153
 *    ---------+--------------+-----------------------------------------
 
154
 *    space    | bdeEfgGiouxX | Leave a space at the start of 
 
155
 *             |              | positive numbers.
 
156
 *    ---------+--------------+-----------------------------------------
 
157
 *    (digit)$ | all          | Specifies the absolute argument number
 
158
 *             |              | for this field. Absolute and relative
 
159
 *             |              | argument numbers cannot be mixed in a
 
160
 *             |              | sprintf string.
 
161
 *    ---------+--------------+-----------------------------------------
 
162
 *     #       | beEfgGoxX    | Use an alternative format. For the
 
163
 *             |              | conversions `o', `x', `X', and `b', 
 
164
 *             |              | prefix the result with ``0'', ``0x'', ``0X'',
 
165
 *             |              |  and ``0b'', respectively. For `e',
 
166
 *             |              | `E', `f', `g', and 'G', force a decimal
 
167
 *             |              | point to be added, even if no digits follow.
 
168
 *             |              | For `g' and 'G', do not remove trailing zeros.
 
169
 *    ---------+--------------+-----------------------------------------
 
170
 *    +        | bdeEfgGiouxX | Add a leading plus sign to positive numbers.
 
171
 *    ---------+--------------+-----------------------------------------
 
172
 *    -        | all          | Left-justify the result of this conversion.
 
173
 *    ---------+--------------+-----------------------------------------
 
174
 *    0 (zero) | bdeEfgGiouxX | Pad with zeros, not spaces.
 
175
 *    ---------+--------------+-----------------------------------------
 
176
 *    *        | all          | Use the next argument as the field width. 
 
177
 *             |              | If negative, left-justify the result. If the
 
178
 *             |              | asterisk is followed by a number and a dollar 
 
179
 *             |              | sign, use the indicated argument as the width.
 
180
 *
 
181
 *     
 
182
 *  The field width is an optional integer, followed optionally by a
 
183
 *  period and a precision. The width specifies the minimum number of
 
184
 *  characters that will be written to the result for this field. For
 
185
 *  numeric fields, the precision controls the number of decimal places
 
186
 *  displayed. For string fields, the precision determines the maximum
 
187
 *  number of characters to be copied from the string. (Thus, the format
 
188
 *  sequence <code>%10.10s</code> will always contribute exactly ten
 
189
 *  characters to the result.)
 
190
 *
 
191
 *  The field types are:
 
192
 *
 
193
 *      Field |  Conversion
 
194
 *      ------+--------------------------------------------------------------
 
195
 *        b   | Convert argument as a binary number.
 
196
 *        c   | Argument is the numeric code for a single character.
 
197
 *        d   | Convert argument as a decimal number.
 
198
 *        E   | Equivalent to `e', but uses an uppercase E to indicate
 
199
 *            | the exponent.
 
200
 *        e   | Convert floating point argument into exponential notation 
 
201
 *            | with one digit before the decimal point. The precision
 
202
 *            | determines the number of fractional digits (defaulting to six).
 
203
 *        f   | Convert floating point argument as [-]ddd.ddd, 
 
204
 *            |  where the precision determines the number of digits after
 
205
 *            | the decimal point.
 
206
 *        G   | Equivalent to `g', but use an uppercase `E' in exponent form.
 
207
 *        g   | Convert a floating point number using exponential form
 
208
 *            | if the exponent is less than -4 or greater than or
 
209
 *            | equal to the precision, or in d.dddd form otherwise.
 
210
 *        i   | Identical to `d'.
 
211
 *        o   | Convert argument as an octal number.
 
212
 *        p   | The valuing of argument.inspect.
 
213
 *        s   | Argument is a string to be substituted. If the format
 
214
 *            | sequence contains a precision, at most that many characters
 
215
 *            | will be copied.
 
216
 *        u   | Treat argument as an unsigned decimal number. Negative integers
 
217
 *            | are displayed as a 32 bit two's complement plus one for the
 
218
 *            | underlying architecture; that is, 2 ** 32 + n.  However, since
 
219
 *            | Ruby has no inherent limit on bits used to represent the
 
220
 *            | integer, this value is preceded by two dots (..) in order to
 
221
 *            | indicate a infinite number of leading sign bits.
 
222
 *        X   | Convert argument as a hexadecimal number using uppercase
 
223
 *            | letters. Negative numbers will be displayed with two
 
224
 *            | leading periods (representing an infinite string of
 
225
 *            | leading 'FF's.
 
226
 *        x   | Convert argument as a hexadecimal number.
 
227
 *            | Negative numbers will be displayed with two
 
228
 *            | leading periods (representing an infinite string of
 
229
 *            | leading 'ff's.
 
230
 *     
 
231
 *  Examples:
 
232
 *
 
233
 *     sprintf("%d %04x", 123, 123)               #=> "123 007b"
 
234
 *     sprintf("%08b '%4s'", 123, 123)            #=> "01111011 ' 123'"
 
235
 *     sprintf("%1$*2$s %2$d %1$s", "hello", 8)   #=> "   hello 8 hello"
 
236
 *     sprintf("%1$*2$s %2$d", "hello", -8)       #=> "hello    -8"
 
237
 *     sprintf("%+g:% g:%-g", 1.23, 1.23, 1.23)   #=> "+1.23: 1.23:1.23"
 
238
 *     sprintf("%u", -123)                        #=> "..4294967173"
 
239
 */
 
240
 
 
241
VALUE
 
242
rb_f_sprintf(argc, argv)
 
243
    int argc;
 
244
    VALUE *argv;
 
245
{
 
246
    VALUE fmt;
 
247
    const char *p, *end;
 
248
    char *buf;
 
249
    int blen, bsiz;
 
250
    VALUE result;
 
251
 
 
252
    int width, prec, flags = FNONE;
 
253
    int nextarg = 1;
 
254
    int posarg = 0;
 
255
    int tainted = 0;
 
256
    VALUE nextvalue;
 
257
    VALUE tmp;
 
258
    VALUE str;
 
259
 
 
260
    fmt = GETNTHARG(0);
 
261
    if (OBJ_TAINTED(fmt)) tainted = 1;
 
262
    StringValue(fmt);
 
263
    fmt = rb_str_new4(fmt);
 
264
    p = RSTRING(fmt)->ptr;
 
265
    end = p + RSTRING(fmt)->len;
 
266
    blen = 0;
 
267
    bsiz = 120;
 
268
    result = rb_str_buf_new(bsiz);
 
269
    buf = RSTRING(result)->ptr;
 
270
 
 
271
    for (; p < end; p++) {
 
272
        const char *t;
 
273
        int n;
 
274
 
 
275
        for (t = p; t < end && *t != '%'; t++) ;
 
276
        PUSH(p, t - p);
 
277
        if (t >= end) {
 
278
            /* end of fmt string */
 
279
            goto sprint_exit;
 
280
        }
 
281
        p = t + 1;              /* skip `%' */
 
282
 
 
283
        width = prec = -1;
 
284
        nextvalue = Qundef;
 
285
      retry:
 
286
        switch (*p) {
 
287
          default:
 
288
            if (ISPRINT(*p))
 
289
                rb_raise(rb_eArgError, "malformed format string - %%%c", *p);
 
290
            else
 
291
                rb_raise(rb_eArgError, "malformed format string");
 
292
            break;
 
293
 
 
294
          case ' ':
 
295
            flags |= FSPACE;
 
296
            p++;
 
297
            goto retry;
 
298
 
 
299
          case '#':
 
300
            flags |= FSHARP;
 
301
            p++;
 
302
            goto retry;
 
303
 
 
304
          case '+':
 
305
            flags |= FPLUS;
 
306
            p++;
 
307
            goto retry;
 
308
 
 
309
          case '-':
 
310
            flags |= FMINUS;
 
311
            p++;
 
312
            goto retry;
 
313
 
 
314
          case '0':
 
315
            flags |= FZERO;
 
316
            p++;
 
317
            goto retry;
 
318
 
 
319
          case '1': case '2': case '3': case '4':
 
320
          case '5': case '6': case '7': case '8': case '9':
 
321
            n = 0;
 
322
            for (; p < end && ISDIGIT(*p); p++) {
 
323
                int next_n = 10 * n + (*p - '0');
 
324
                if (next_n / 10 != n) {
 
325
                    rb_raise(rb_eArgError, "width too big");
 
326
                }
 
327
                n = 10 * n + (*p - '0');
 
328
            }
 
329
            if (p >= end) {
 
330
                rb_raise(rb_eArgError, "malformed format string - %%[0-9]");
 
331
            }
 
332
            if (*p == '$') {
 
333
                if (nextvalue != Qundef) {
 
334
                    rb_raise(rb_eArgError, "value given twice - %d$", n);
 
335
                }
 
336
                nextvalue = GETPOSARG(n);
 
337
                p++;
 
338
                goto retry;
 
339
            }
 
340
            width = n;
 
341
            flags |= FWIDTH;
 
342
            goto retry;
 
343
 
 
344
          case '*':
 
345
            if (flags & FWIDTH) {
 
346
                rb_raise(rb_eArgError, "width given twice");
 
347
            }
 
348
 
 
349
            flags |= FWIDTH;
 
350
            GETASTER(width);
 
351
            if (width < 0) {
 
352
                flags |= FMINUS;
 
353
                width = -width;
 
354
            }
 
355
            p++;
 
356
            goto retry;
 
357
 
 
358
          case '.':
 
359
            if (flags & FPREC) {
 
360
                rb_raise(rb_eArgError, "precision given twice");
 
361
            }
 
362
            flags |= FPREC;
 
363
 
 
364
            prec = 0;
 
365
            p++;
 
366
            if (*p == '*') {
 
367
                GETASTER(prec);
 
368
                if (prec < 0) { /* ignore negative precision */
 
369
                    flags &= ~FPREC;
 
370
                }
 
371
                p++;
 
372
                goto retry;
 
373
            }
 
374
 
 
375
            for (; p < end && ISDIGIT(*p); p++) {
 
376
                prec = 10 * prec + (*p - '0');
 
377
            }
 
378
            if (p >= end) {
 
379
                rb_raise(rb_eArgError, "malformed format string - %%.[0-9]");
 
380
            }
 
381
            goto retry;
 
382
 
 
383
          case '\n':
 
384
            p--;
 
385
          case '\0':
 
386
          case '%':
 
387
            if (flags != FNONE) {
 
388
                rb_raise(rb_eArgError, "illegal format character - %%");
 
389
            }
 
390
            PUSH("%", 1);
 
391
            break;
 
392
 
 
393
          case 'c':
 
394
            {
 
395
                VALUE val = GETARG();
 
396
                char c;
 
397
 
 
398
                if (!(flags & FMINUS))
 
399
                    while (--width > 0)
 
400
                        PUSH(" ", 1);
 
401
                c = NUM2INT(val) & 0xff;
 
402
                PUSH(&c, 1);
 
403
                while (--width > 0)
 
404
                    PUSH(" ", 1);
 
405
            }
 
406
            break;
 
407
 
 
408
          case 's':
 
409
          case 'p':
 
410
            {
 
411
                VALUE arg = GETARG();
 
412
                long len;
 
413
 
 
414
                if (*p == 'p') arg = rb_inspect(arg);
 
415
                str = rb_obj_as_string(arg);
 
416
                if (OBJ_TAINTED(str)) tainted = 1;
 
417
                len = RSTRING(str)->len;
 
418
                if (flags&FPREC) {
 
419
                    if (prec < len) {
 
420
                        len = prec;
 
421
                    }
 
422
                }
 
423
                /* need to adjust multi-byte string pos */
 
424
                if (flags&FWIDTH) {
 
425
                    if (width > len) {
 
426
                        CHECK(width);
 
427
                        width -= len;
 
428
                        if (!(flags&FMINUS)) {
 
429
                            while (width--) {
 
430
                                buf[blen++] = ' ';
 
431
                            }
 
432
                        }
 
433
                        memcpy(&buf[blen], RSTRING_PTR(str), len);
 
434
                        blen += len;
 
435
                        if (flags&FMINUS) {
 
436
                            while (width--) {
 
437
                                buf[blen++] = ' ';
 
438
                            }
 
439
                        }
 
440
                        break;
 
441
                    }
 
442
                }
 
443
                PUSH(RSTRING(str)->ptr, len);
 
444
            }
 
445
            break;
 
446
 
 
447
          case 'd':
 
448
          case 'i':
 
449
          case 'o':
 
450
          case 'x':
 
451
          case 'X':
 
452
          case 'b':
 
453
          case 'B':
 
454
          case 'u':
 
455
            {
 
456
                volatile VALUE val = GETARG();
 
457
                char fbuf[32], nbuf[64], *s, *t;
 
458
                char *prefix = 0;
 
459
                int sign = 0;
 
460
                char sc = 0;
 
461
                long v = 0;
 
462
                int base, bignum = 0;
 
463
                int len, pos;
 
464
                VALUE tmp;
 
465
                volatile VALUE tmp1;
 
466
 
 
467
                switch (*p) {
 
468
                  case 'd':
 
469
                  case 'i':
 
470
                    sign = 1; break;
 
471
                  case 'o':
 
472
                  case 'x':
 
473
                  case 'X':
 
474
                  case 'b':
 
475
                  case 'B':
 
476
                  case 'u':
 
477
                  default:
 
478
                    if (flags&(FPLUS|FSPACE)) sign = 1;
 
479
                    break;
 
480
                }
 
481
                if (flags & FSHARP) {
 
482
                    switch (*p) {
 
483
                      case 'o':
 
484
                        prefix = "0"; break;
 
485
                      case 'x':
 
486
                        prefix = "0x"; break;
 
487
                      case 'X':
 
488
                        prefix = "0X"; break;
 
489
                      case 'b':
 
490
                        prefix = "0b"; break;
 
491
                      case 'B':
 
492
                        prefix = "0B"; break;
 
493
                    }
 
494
                    if (prefix) {
 
495
                        width -= strlen(prefix);
 
496
                    }
 
497
                }
 
498
 
 
499
              bin_retry:
 
500
                switch (TYPE(val)) {
 
501
                  case T_FLOAT:
 
502
                    val = rb_dbl2big(RFLOAT(val)->value);
 
503
                    if (FIXNUM_P(val)) goto bin_retry;
 
504
                    bignum = 1;
 
505
                    break;
 
506
                  case T_STRING:
 
507
                    val = rb_str_to_inum(val, 0, Qtrue);
 
508
                    goto bin_retry;
 
509
                  case T_BIGNUM:
 
510
                    bignum = 1;
 
511
                    break;
 
512
                  case T_FIXNUM:
 
513
                    v = FIX2LONG(val);
 
514
                    break;
 
515
                  default:
 
516
                    val = rb_Integer(val);
 
517
                    goto bin_retry;
 
518
                }
 
519
 
 
520
                switch (*p) {
 
521
                  case 'o':
 
522
                    base = 8; break;
 
523
                  case 'x':
 
524
                  case 'X':
 
525
                    base = 16; break;
 
526
                  case 'b':
 
527
                  case 'B':
 
528
                    base = 2; break;
 
529
                  case 'u':
 
530
                  case 'd':
 
531
                  case 'i':
 
532
                  default:
 
533
                    base = 10; break;
 
534
                }
 
535
                if (!bignum) {
 
536
                    if (base == 2) {
 
537
                        val = rb_int2big(v);
 
538
                        goto bin_retry;
 
539
                    }
 
540
                    if (sign) {
 
541
                        char c = *p;
 
542
                        if (c == 'i') c = 'd'; /* %d and %i are identical */
 
543
                        if (v < 0) {
 
544
                            v = -v;
 
545
                            sc = '-';
 
546
                            width--;
 
547
                        }
 
548
                        else if (flags & FPLUS) {
 
549
                            sc = '+';
 
550
                            width--;
 
551
                        }
 
552
                        else if (flags & FSPACE) {
 
553
                            sc = ' ';
 
554
                            width--;
 
555
                        }
 
556
                        sprintf(fbuf, "%%l%c", c);
 
557
                        sprintf(nbuf, fbuf, v);
 
558
                        s = nbuf;
 
559
                        goto format_integer;
 
560
                    }
 
561
                    s = nbuf;
 
562
                    if (v < 0) {
 
563
                        if (base == 10) {
 
564
                            rb_warning("negative number for %%u specifier");
 
565
                        }
 
566
                        if (!(flags&(FPREC|FZERO))) {
 
567
                            strcpy(s, "..");
 
568
                            s += 2;
 
569
                        }
 
570
                    }
 
571
                    sprintf(fbuf, "%%l%c", *p == 'X' ? 'x' : *p);
 
572
                    sprintf(s, fbuf, v);
 
573
                    if (v < 0) {
 
574
                        char d = 0;
 
575
 
 
576
                        remove_sign_bits(s, base);
 
577
                        switch (base) {
 
578
                          case 16:
 
579
                            d = 'f'; break;
 
580
                          case 8:
 
581
                            d = '7'; break;
 
582
                        }
 
583
                        if (d && *s != d) {
 
584
                            memmove(s+1, s, strlen(s)+1);
 
585
                            *s = d;
 
586
                        }
 
587
                    }
 
588
                    s = nbuf;
 
589
                    goto format_integer;
 
590
                }
 
591
 
 
592
                if (sign) {
 
593
                    tmp = rb_big2str(val, base);
 
594
                    s = RSTRING(tmp)->ptr;
 
595
                    if (s[0] == '-') {
 
596
                        s++;
 
597
                        sc = '-';
 
598
                        width--;
 
599
                    }
 
600
                    else if (flags & FPLUS) {
 
601
                        sc = '+';
 
602
                        width--;
 
603
                    }
 
604
                    else if (flags & FSPACE) {
 
605
                        sc = ' ';
 
606
                        width--;
 
607
                    }
 
608
                    goto format_integer;
 
609
                }
 
610
                if (!RBIGNUM(val)->sign) {
 
611
                    val = rb_big_clone(val);
 
612
                    rb_big_2comp(val);
 
613
                }
 
614
                tmp1 = tmp = rb_big2str0(val, base, RBIGNUM(val)->sign);
 
615
                s = RSTRING(tmp)->ptr;
 
616
                if (*s == '-') {
 
617
                    if (base == 10) {
 
618
                        rb_warning("negative number for %%u specifier");
 
619
                    }
 
620
                    remove_sign_bits(++s, base);
 
621
                    tmp = rb_str_new(0, 3+strlen(s));
 
622
                    t = RSTRING(tmp)->ptr;
 
623
                    if (!(flags&(FPREC|FZERO))) {
 
624
                        strcpy(t, "..");
 
625
                        t += 2;
 
626
                    }
 
627
                    switch (base) {
 
628
                      case 16:
 
629
                        if (s[0] != 'f') strcpy(t++, "f"); break;
 
630
                      case 8:
 
631
                        if (s[0] != '7') strcpy(t++, "7"); break;
 
632
                      case 2:
 
633
                        if (s[0] != '1') strcpy(t++, "1"); break;
 
634
                    }
 
635
                    strcpy(t, s);
 
636
                    bignum = 2;
 
637
                }
 
638
                s = RSTRING(tmp)->ptr;
 
639
 
 
640
              format_integer:
 
641
                pos = -1;
 
642
                len = strlen(s);
 
643
 
 
644
                if (*p == 'X') {
 
645
                    char *pp = s;
 
646
                    while (*pp) {
 
647
                        *pp = toupper(*pp);
 
648
                        pp++;
 
649
                    }
 
650
                }
 
651
                if ((flags&(FZERO|FPREC)) == FZERO) {
 
652
                    prec = width;
 
653
                    width = 0;
 
654
                }
 
655
                else {
 
656
                    if (prec < len) prec = len;
 
657
                    width -= prec;
 
658
                }
 
659
                if (!(flags&FMINUS)) {
 
660
                    CHECK(width);
 
661
                    while (width-- > 0) {
 
662
                        buf[blen++] = ' ';
 
663
                    }
 
664
                }
 
665
                if (sc) PUSH(&sc, 1);
 
666
                if (prefix) {
 
667
                    int plen = strlen(prefix);
 
668
                    PUSH(prefix, plen);
 
669
                }
 
670
                CHECK(prec - len);
 
671
                if (!bignum && v < 0) {
 
672
                    char c = sign_bits(base, p);
 
673
                    while (len < prec--) {
 
674
                        buf[blen++] = c;
 
675
                    }
 
676
                }
 
677
                else {
 
678
                    char c;
 
679
 
 
680
                    if (bignum && !RBIGNUM(val)->sign)
 
681
                        c = sign_bits(base, p);
 
682
                    else
 
683
                        c = '0';
 
684
                    while (len < prec--) {
 
685
                        buf[blen++] = c;
 
686
                    }
 
687
                }
 
688
                PUSH(s, len);
 
689
                CHECK(width);
 
690
                while (width-- > 0) {
 
691
                    buf[blen++] = ' ';
 
692
                }
 
693
            }
 
694
            break;
 
695
 
 
696
          case 'f':
 
697
          case 'g':
 
698
          case 'G':
 
699
          case 'e':
 
700
          case 'E':
 
701
            {
 
702
                VALUE val = GETARG();
 
703
                double fval;
 
704
                int i, need = 6;
 
705
                char fbuf[32];
 
706
 
 
707
                fval = RFLOAT(rb_Float(val))->value;
 
708
#if defined(_WIN32) && !defined(__BORLANDC__)
 
709
                if (isnan(fval) || isinf(fval)) {
 
710
                    char *expr;
 
711
 
 
712
                    if  (isnan(fval)) {
 
713
                        expr = "NaN";
 
714
                    }
 
715
                    else {
 
716
                        expr = "Inf";
 
717
                    }
 
718
                    need = strlen(expr);
 
719
                    if ((!isnan(fval) && fval < 0.0) || (flags & FPLUS))
 
720
                        need++;
 
721
                    if ((flags & FWIDTH) && need < width)
 
722
                        need = width;
 
723
 
 
724
                    CHECK(need);
 
725
                    sprintf(&buf[blen], "%*s", need, "");
 
726
                    if (flags & FMINUS) {
 
727
                        if (!isnan(fval) && fval < 0.0)
 
728
                            buf[blen++] = '-';
 
729
                        else if (flags & FPLUS)
 
730
                            buf[blen++] = '+';
 
731
                        else if (flags & FSPACE)
 
732
                            blen++;
 
733
                        strncpy(&buf[blen], expr, strlen(expr));
 
734
                    }
 
735
                    else if (flags & FZERO) {
 
736
                        if (!isnan(fval) && fval < 0.0) {
 
737
                            buf[blen++] = '-';
 
738
                            need--;
 
739
                        }
 
740
                        else if (flags & FPLUS) {
 
741
                            buf[blen++] = '+';
 
742
                            need--;
 
743
                        }
 
744
                        else if (flags & FSPACE) {
 
745
                            blen++;
 
746
                            need--;
 
747
                        }
 
748
                        while (need-- - strlen(expr) > 0) {
 
749
                            buf[blen++] = '0';
 
750
                        }
 
751
                        strncpy(&buf[blen], expr, strlen(expr));
 
752
                    }
 
753
                    else {
 
754
                        if (!isnan(fval) && fval < 0.0)
 
755
                            buf[blen + need - strlen(expr) - 1] = '-';
 
756
                        else if (flags & FPLUS)
 
757
                            buf[blen + need - strlen(expr) - 1] = '+';
 
758
                        strncpy(&buf[blen + need - strlen(expr)], expr,
 
759
                                strlen(expr));
 
760
                    }
 
761
                    blen += strlen(&buf[blen]);
 
762
                    break;
 
763
                }
 
764
#endif  /* defined(_WIN32) && !defined(__BORLANDC__) */
 
765
                fmt_setup(fbuf, *p, flags, width, prec);
 
766
                need = 0;
 
767
                if (*p != 'e' && *p != 'E') {
 
768
                    i = INT_MIN;
 
769
                    frexp(fval, &i);
 
770
                    if (i > 0)
 
771
                        need = BIT_DIGITS(i);
 
772
                }
 
773
                need += (flags&FPREC) ? prec : 6;
 
774
                if ((flags&FWIDTH) && need < width)
 
775
                    need = width;
 
776
                need += 20;
 
777
 
 
778
                CHECK(need);
 
779
                sprintf(&buf[blen], fbuf, fval);
 
780
                blen += strlen(&buf[blen]);
 
781
            }
 
782
            break;
 
783
        }
 
784
        flags = FNONE;
 
785
    }
 
786
 
 
787
  sprint_exit:
 
788
    /* XXX - We cannot validiate the number of arguments if (digit)$ style used.
 
789
     */
 
790
    if (posarg >= 0 && nextarg < argc) {
 
791
        const char *mesg = "too many arguments for format string";
 
792
        if (RTEST(ruby_debug)) rb_raise(rb_eArgError, mesg);
 
793
        if (RTEST(ruby_verbose)) rb_warn(mesg);
 
794
    }
 
795
    rb_str_resize(result, blen);
 
796
 
 
797
    if (tainted) OBJ_TAINT(result);
 
798
    return result;
 
799
}
 
800
 
 
801
static void
 
802
fmt_setup(buf, c, flags, width, prec)
 
803
    char *buf;
 
804
    int c;
 
805
    int flags, width, prec;
 
806
{
 
807
    *buf++ = '%';
 
808
    if (flags & FSHARP) *buf++ = '#';
 
809
    if (flags & FPLUS)  *buf++ = '+';
 
810
    if (flags & FMINUS) *buf++ = '-';
 
811
    if (flags & FZERO)  *buf++ = '0';
 
812
    if (flags & FSPACE) *buf++ = ' ';
 
813
 
 
814
    if (flags & FWIDTH) {
 
815
        sprintf(buf, "%d", width);
 
816
        buf += strlen(buf);
 
817
    }
 
818
 
 
819
    if (flags & FPREC) {
 
820
        sprintf(buf, ".%d", prec);
 
821
        buf += strlen(buf);
 
822
    }
 
823
 
 
824
    *buf++ = c;
 
825
    *buf = '\0';
 
826
}