Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 1124 lines (1042 sloc) 29.684 kb
511dc44 initial import
Laurent Sansonetti authored
1 /**********************************************************************
2
3 sprintf.c -
4
eee9d7b sync with ruby trunk r15665
Laurent Sansonetti authored
5 $Author: akr $
511dc44 initial import
Laurent Sansonetti authored
6 created at: Fri Oct 15 10:39:26 JST 1993
7
8 Copyright (C) 1993-2007 Yukihiro Matsumoto
9 Copyright (C) 2000 Network Applied Communication Laboratory, Inc.
10 Copyright (C) 2000 Information-technology Promotion Agency, Japan
11
12 **********************************************************************/
13
14 #include "ruby/ruby.h"
15 #include "ruby/re.h"
16 #include "ruby/encoding.h"
17 #include <math.h>
18 #include <stdarg.h>
19
20 #define BIT_DIGITS(N) (((N)*146)/485 + 1) /* log2(10) =~ 146/485 */
21 #define BITSPERDIG (SIZEOF_BDIGITS*CHAR_BIT)
22 #define EXTENDSIGN(n, l) (((~0 << (n)) >> (((n)*(l)) % BITSPERDIG)) & ~(~0 << (n)))
23
26d0e1f merge with ruby trunk r16762 + better/faster objc-like dispatcher
Laurent Sansonetti authored
24 #if !WITH_OBJC
511dc44 initial import
Laurent Sansonetti authored
25 static void fmt_setup(char*,int,int,int,int);
26
27 static char*
28 remove_sign_bits(char *str, int base)
29 {
30 char *s, *t;
31
32 s = t = str;
33
34 if (base == 16) {
35 while (*t == 'f') {
36 t++;
37 }
38 }
39 else if (base == 8) {
40 *t |= EXTENDSIGN(3, strlen(t));
41 while (*t == '7') {
42 t++;
43 }
44 }
45 else if (base == 2) {
46 while (*t == '1') {
47 t++;
48 }
49 }
50
51 return t;
52 }
53
54 static char
55 sign_bits(int base, const char *p)
56 {
57 char c = '.';
58
59 switch (base) {
60 case 16:
61 if (*p == 'X') c = 'F';
62 else c = 'f';
63 break;
64 case 8:
65 c = '7'; break;
66 case 2:
67 c = '1'; break;
68 }
69 return c;
70 }
26d0e1f merge with ruby trunk r16762 + better/faster objc-like dispatcher
Laurent Sansonetti authored
71 #endif
511dc44 initial import
Laurent Sansonetti authored
72
73 #define FNONE 0
74 #define FSHARP 1
75 #define FMINUS 2
76 #define FPLUS 4
77 #define FZERO 8
78 #define FSPACE 16
79 #define FWIDTH 32
80 #define FPREC 64
81 #define FPREC0 128
82
83 #define CHECK(l) do {\
84 while (blen + (l) >= bsiz) {\
85 bsiz*=2;\
86 }\
87 rb_str_resize(result, bsiz);\
70fef6e RSTRING_PTR/RSTRING_LEN -> RSTRING_BYTEPTR/RSTRING_BYTELEN, RSTRING_C…
Laurent Sansonetti authored
88 buf = RSTRING_BYTEPTR(result);\
511dc44 initial import
Laurent Sansonetti authored
89 } while (0)
90
91 #define PUSH(s, l) do { \
92 CHECK(l);\
93 memcpy(&buf[blen], s, l);\
94 blen += (l);\
95 } while (0)
96
97 #define FILL(c, l) do { \
98 CHECK(l);\
99 memset(&buf[blen], c, l);\
100 blen += (l);\
101 } while (0)
102
103 #define GETARG() (nextvalue != Qundef ? nextvalue : \
104 posarg < 0 ? \
105 (rb_raise(rb_eArgError, "unnumbered(%d) mixed with numbered", nextarg), 0) : \
106 (posarg = nextarg++, GETNTHARG(posarg)))
107
108 #define GETPOSARG(n) (posarg > 0 ? \
109 (rb_raise(rb_eArgError, "numbered(%d) after unnumbered(%d)", n, posarg), 0) : \
110 ((n < 1) ? (rb_raise(rb_eArgError, "invalid index - %d$", n), 0) : \
111 (posarg = -1, GETNTHARG(n))))
112
113 #define GETNTHARG(nth) \
114 ((nth >= argc) ? (rb_raise(rb_eArgError, "too few arguments"), 0) : argv[nth])
115
116 #define GETNUM(n, val) \
117 for (; p < end && rb_enc_isdigit(*p, enc); p++) { \
118 int next_n = 10 * n + (*p - '0'); \
119 if (next_n / 10 != n) {\
120 rb_raise(rb_eArgError, #val " too big"); \
121 } \
122 n = next_n; \
123 } \
124 if (p >= end) { \
125 rb_raise(rb_eArgError, "malformed format string - %%*[0-9]"); \
126 }
127
128 #define GETASTER(val) do { \
129 t = p++; \
130 n = 0; \
131 GETNUM(n, val); \
132 if (*p == '$') { \
133 tmp = GETPOSARG(n); \
134 } \
135 else { \
136 tmp = GETARG(); \
137 p = t; \
138 } \
139 val = NUM2INT(tmp); \
140 } while (0)
141
142
143 /*
144 * call-seq:
145 * format(format_string [, arguments...] ) => string
146 * sprintf(format_string [, arguments...] ) => string
147 *
148 * Returns the string resulting from applying <i>format_string</i> to
eee9d7b sync with ruby trunk r15665
Laurent Sansonetti authored
149 * any additional arguments. Within the format string, any characters
150 * other than format sequences are copied to the result.
151 *
152 * The syntax of a format sequence is follows.
153 *
154 * %[flags][width][.precision]type
155 *
156 * A format
511dc44 initial import
Laurent Sansonetti authored
157 * sequence consists of a percent sign, followed by optional flags,
158 * width, and precision indicators, then terminated with a field type
eee9d7b sync with ruby trunk r15665
Laurent Sansonetti authored
159 * character. The field type controls how the corresponding
511dc44 initial import
Laurent Sansonetti authored
160 * <code>sprintf</code> argument is to be interpreted, while the flags
eee9d7b sync with ruby trunk r15665
Laurent Sansonetti authored
161 * modify that interpretation.
511dc44 initial import
Laurent Sansonetti authored
162 *
eee9d7b sync with ruby trunk r15665
Laurent Sansonetti authored
163 * The field type characters are:
511dc44 initial import
Laurent Sansonetti authored
164 *
eee9d7b sync with ruby trunk r15665
Laurent Sansonetti authored
165 * Field | Integer Format
511dc44 initial import
Laurent Sansonetti authored
166 * ------+--------------------------------------------------------------
167 * b | Convert argument as a binary number.
eee9d7b sync with ruby trunk r15665
Laurent Sansonetti authored
168 * | Negative numbers will be displayed as a two's complement
169 * | prefixed with `..1'.
170 * B | Equivalent to `b', but uses an uppercase 0B for prefix
171 * | in the alternative format by #.
511dc44 initial import
Laurent Sansonetti authored
172 * d | Convert argument as a decimal number.
eee9d7b sync with ruby trunk r15665
Laurent Sansonetti authored
173 * i | Identical to `d'.
174 * o | Convert argument as an octal number.
175 * | Negative numbers will be displayed as a two's complement
176 * | prefixed with `..7'.
177 * u | Identical to `d'.
178 * x | Convert argument as a hexadecimal number.
179 * | Negative numbers will be displayed as a two's complement
180 * | prefixed with `..f' (representing an infinite string of
181 * | leading 'ff's).
182 * X | Equivalent to `x', but uses uppercase letters.
183 *
184 * Field | Float Format
185 * ------+--------------------------------------------------------------
186 * e | Convert floating point argument into exponential notation
187 * | with one digit before the decimal point as [-]d.dddddde[+-]dd.
188 * | The precision specifies the number of digits after the decimal
189 * | point (defaulting to six).
511dc44 initial import
Laurent Sansonetti authored
190 * E | Equivalent to `e', but uses an uppercase E to indicate
191 * | the exponent.
eee9d7b sync with ruby trunk r15665
Laurent Sansonetti authored
192 * f | Convert floating point argument as [-]ddd.dddddd,
193 * | where the precision specifies the number of digits after
511dc44 initial import
Laurent Sansonetti authored
194 * | the decimal point.
195 * g | Convert a floating point number using exponential form
196 * | if the exponent is less than -4 or greater than or
eee9d7b sync with ruby trunk r15665
Laurent Sansonetti authored
197 * | equal to the precision, or in dd.dddd form otherwise.
198 * | The precision specifies the number of significant digits.
199 * G | Equivalent to `g', but use an uppercase `E' in exponent form.
200 *
201 * Field | Other Format
202 * ------+--------------------------------------------------------------
203 * c | Argument is the numeric code for a single character or
204 * | a single character string itself.
511dc44 initial import
Laurent Sansonetti authored
205 * p | The valuing of argument.inspect.
eee9d7b sync with ruby trunk r15665
Laurent Sansonetti authored
206 * s | Argument is a string to be substituted. If the format
511dc44 initial import
Laurent Sansonetti authored
207 * | sequence contains a precision, at most that many characters
208 * | will be copied.
eee9d7b sync with ruby trunk r15665
Laurent Sansonetti authored
209 *
210 * The flags modifies the behavior of the formats.
211 * The flag characters are:
212 *
213 * Flag | Applies to | Meaning
214 * ---------+---------------+-----------------------------------------
215 * space | bBdiouxX | Leave a space at the start of
216 * | eEfgG | non-negative numbers.
217 * | (numeric fmt) | For `o', `x', `X', `b' and `B', use
218 * | | a minus sign with absolute value for
219 * | | negative values.
220 * ---------+---------------+-----------------------------------------
221 * (digit)$ | all | Specifies the absolute argument number
222 * | | for this field. Absolute and relative
223 * | | argument numbers cannot be mixed in a
224 * | | sprintf string.
225 * ---------+---------------+-----------------------------------------
226 * # | bBoxX | Use an alternative format.
227 * | eEfgG | For the conversions `o', increase the precision
228 * | | until the first digit will be `0' if
229 * | | it is not formatted as complements.
230 * | | For the conversions `x', `X', `b' and `B'
231 * | | on non-zero, prefix the result with ``0x'',
232 * | | ``0X'', ``0b'' and ``0B'', respectively.
233 * | | For `e', `E', `f', `g', and 'G',
234 * | | force a decimal point to be added,
235 * | | even if no digits follow.
236 * | | For `g' and 'G', do not remove trailing zeros.
237 * ---------+---------------+-----------------------------------------
238 * + | bBdiouxX | Add a leading plus sign to non-negative
239 * | eEfgG | numbers.
240 * | (numeric fmt) | For `o', `x', `X', `b' and `B', use
241 * | | a minus sign with absolute value for
242 * | | negative values.
243 * ---------+---------------+-----------------------------------------
244 * - | all | Left-justify the result of this conversion.
245 * ---------+---------------+-----------------------------------------
246 * 0 (zero) | bBdiouxX | Pad with zeros, not spaces.
247 * | eEfgG | For `o', `x', `X', `b' and `B', radix-1
248 * | (numeric fmt) | is used for negative numbers formatted as
249 * | | complements.
250 * ---------+---------------+-----------------------------------------
251 * * | all | Use the next argument as the field width.
252 * | | If negative, left-justify the result. If the
253 * | | asterisk is followed by a number and a dollar
254 * | | sign, use the indicated argument as the width.
255 *
256 * Examples of flags:
257 *
258 * # `+' and space flag specifies the sign of non-negative numbers.
259 * sprintf("%d", 123) #=> "123"
260 * sprintf("%+d", 123) #=> "+123"
261 * sprintf("% d", 123) #=> " 123"
262 *
263 * # `#' flag for `o' increases number of digits to show `0'.
264 * # `+' and space flag changes format of negative numbers.
265 * sprintf("%o", 123) #=> "173"
266 * sprintf("%#o", 123) #=> "0173"
267 * sprintf("%+o", -123) #=> "-173"
268 * sprintf("%o", -123) #=> "..7605"
269 * sprintf("%#o", -123) #=> "..7605"
270 *
271 * # `#' flag for `x' add a prefix `0x' for non-zero numbers.
272 * # `+' and space flag disables complements for negative numbers.
273 * sprintf("%x", 123) #=> "7b"
274 * sprintf("%#x", 123) #=> "0x7b"
275 * sprintf("%+x", -123) #=> "-7b"
276 * sprintf("%x", -123) #=> "..f85"
277 * sprintf("%#x", -123) #=> "0x..f85"
278 * sprintf("%#x", 0) #=> "0"
279 *
280 * # `#' for `X' uses the prefix `0X'.
281 * sprintf("%X", 123) #=> "7B"
282 * sprintf("%#X", 123) #=> "0X7B"
283 *
284 * # `#' flag for `b' add a prefix `0b' for non-zero numbers.
285 * # `+' and space flag disables complements for negative numbers.
286 * sprintf("%b", 123) #=> "1111011"
287 * sprintf("%#b", 123) #=> "0b1111011"
288 * sprintf("%+b", -123) #=> "-1111011"
289 * sprintf("%b", -123) #=> "..10000101"
290 * sprintf("%#b", -123) #=> "0b..10000101"
291 * sprintf("%#b", 0) #=> "0"
292 *
293 * # `#' for `B' uses the prefix `0B'.
294 * sprintf("%B", 123) #=> "1111011"
295 * sprintf("%#B", 123) #=> "0B1111011"
296 *
297 * # `#' for `e' forces to show the decimal point.
298 * sprintf("%.0e", 1) #=> "1e+00"
299 * sprintf("%#.0e", 1) #=> "1.e+00"
300 *
301 * # `#' for `f' forces to show the decimal point.
302 * sprintf("%.0f", 1234) #=> "1234"
303 * sprintf("%#.0f", 1234) #=> "1234."
304 *
305 * # `#' for `g' forces to show the decimal point.
306 * # It also disables stripping lowest zeros.
307 * sprintf("%g", 123.4) #=> "123.4"
308 * sprintf("%#g", 123.4) #=> "123.400"
309 * sprintf("%g", 123456) #=> "123456"
310 * sprintf("%#g", 123456) #=> "123456."
311 *
312 * The field width is an optional integer, followed optionally by a
313 * period and a precision. The width specifies the minimum number of
314 * characters that will be written to the result for this field.
315 *
316 * Examples of width:
317 *
318 * # padding is done by spaces, width=20
319 * # 0 or radix-1. <------------------>
320 * sprintf("%20d", 123) #=> " 123"
321 * sprintf("%+20d", 123) #=> " +123"
322 * sprintf("%020d", 123) #=> "00000000000000000123"
323 * sprintf("%+020d", 123) #=> "+0000000000000000123"
324 * sprintf("% 020d", 123) #=> " 0000000000000000123"
325 * sprintf("%-20d", 123) #=> "123 "
326 * sprintf("%-+20d", 123) #=> "+123 "
327 * sprintf("%- 20d", 123) #=> " 123 "
328 * sprintf("%020x", -123) #=> "..ffffffffffffffff85"
329 *
330 * For
331 * numeric fields, the precision controls the number of decimal places
332 * displayed. For string fields, the precision determines the maximum
333 * number of characters to be copied from the string. (Thus, the format
334 * sequence <code>%10.10s</code> will always contribute exactly ten
335 * characters to the result.)
336 *
337 * Examples of precisions:
338 *
339 * # precision for `d', 'o', 'x' and 'b' is
340 * # minimum number of digits <------>
341 * sprintf("%20.8d", 123) #=> " 00000123"
342 * sprintf("%20.8o", 123) #=> " 00000173"
343 * sprintf("%20.8x", 123) #=> " 0000007b"
344 * sprintf("%20.8b", 123) #=> " 01111011"
345 * sprintf("%20.8d", -123) #=> " -00000123"
346 * sprintf("%20.8o", -123) #=> " ..777605"
347 * sprintf("%20.8x", -123) #=> " ..ffff85"
348 * sprintf("%20.8b", -11) #=> " ..110101"
349 *
350 * # "0x" and "0b" for `#x' and `#b' is not counted for
351 * # precision but "0" for `#o' is counted. <------>
352 * sprintf("%#20.8d", 123) #=> " 00000123"
353 * sprintf("%#20.8o", 123) #=> " 00000173"
354 * sprintf("%#20.8x", 123) #=> " 0x0000007b"
355 * sprintf("%#20.8b", 123) #=> " 0b01111011"
356 * sprintf("%#20.8d", -123) #=> " -00000123"
357 * sprintf("%#20.8o", -123) #=> " ..777605"
358 * sprintf("%#20.8x", -123) #=> " 0x..ffff85"
359 * sprintf("%#20.8b", -11) #=> " 0b..110101"
360 *
361 * # precision for `e' is number of
362 * # digits after the decimal point <------>
363 * sprintf("%20.8e", 1234.56789) #=> " 1.23456789e+03"
364 *
365 * # precision for `f' is number of
366 * # digits after the decimal point <------>
367 * sprintf("%20.8f", 1234.56789) #=> " 1234.56789000"
368 *
369 * # precision for `g' is number of
370 * # significant digits <------->
371 * sprintf("%20.8g", 1234.56789) #=> " 1234.5679"
372 *
373 * # <------->
374 * sprintf("%20.8g", 123456789) #=> " 1.2345679e+08"
375 *
376 * # precision for `s' is
377 * # maximum number of characters <------>
378 * sprintf("%20.8s", "string test") #=> " string t"
511dc44 initial import
Laurent Sansonetti authored
379 *
380 * Examples:
381 *
382 * sprintf("%d %04x", 123, 123) #=> "123 007b"
383 * sprintf("%08b '%4s'", 123, 123) #=> "01111011 ' 123'"
384 * sprintf("%1$*2$s %2$d %1$s", "hello", 8) #=> " hello 8 hello"
385 * sprintf("%1$*2$s %2$d", "hello", -8) #=> "hello -8"
386 * sprintf("%+g:% g:%-g", 1.23, 1.23, 1.23) #=> "+1.23: 1.23:1.23"
387 * sprintf("%u", -123) #=> "-123"
388 */
389
390 VALUE
391 rb_f_sprintf(int argc, const VALUE *argv)
392 {
393 return rb_str_format(argc - 1, argv + 1, GETNTHARG(0));
394 }
395
8240301 new string format implementation
Laurent Sansonetti authored
396 #if !WITH_OBJC
511dc44 initial import
Laurent Sansonetti authored
397 VALUE
398 rb_str_format(int argc, const VALUE *argv, VALUE fmt)
399 {
400 rb_encoding *enc;
401 const char *p, *end;
402 char *buf;
403 int blen, bsiz;
404 VALUE result;
405
406 int width, prec, flags = FNONE;
407 int nextarg = 1;
408 int posarg = 0;
409 int tainted = 0;
410 VALUE nextvalue;
411 VALUE tmp;
412 VALUE str;
413
414 #define CHECK_FOR_WIDTH(f) \
415 if ((f) & FWIDTH) { \
416 rb_raise(rb_eArgError, "width given twice"); \
417 } \
418 if ((f) & FPREC0) { \
419 rb_raise(rb_eArgError, "width after precision"); \
420 }
421 #define CHECK_FOR_FLAGS(f) \
422 if ((f) & FWIDTH) { \
423 rb_raise(rb_eArgError, "flag after width"); \
424 } \
425 if ((f) & FPREC0) { \
426 rb_raise(rb_eArgError, "flag after precision"); \
427 }
428
429 ++argc;
430 --argv;
431 if (OBJ_TAINTED(fmt)) tainted = 1;
432 StringValue(fmt);
433 enc = rb_enc_get(fmt);
434 fmt = rb_str_new4(fmt);
70fef6e RSTRING_PTR/RSTRING_LEN -> RSTRING_BYTEPTR/RSTRING_BYTELEN, RSTRING_C…
Laurent Sansonetti authored
435 p = RSTRING_BYTEPTR(fmt); /* ok */
436 end = p + RSTRING_BYTELEN(fmt);
511dc44 initial import
Laurent Sansonetti authored
437 blen = 0;
438 bsiz = 120;
439 result = rb_str_buf_new(bsiz);
440 rb_enc_copy(result, fmt);
70fef6e RSTRING_PTR/RSTRING_LEN -> RSTRING_BYTEPTR/RSTRING_BYTELEN, RSTRING_C…
Laurent Sansonetti authored
441 buf = RSTRING_BYTEPTR(result);
511dc44 initial import
Laurent Sansonetti authored
442 memset(buf, 0, bsiz);
443
444 for (; p < end; p++) {
445 const char *t;
446 int n;
447
448 for (t = p; t < end && *t != '%'; t++) ;
449 PUSH(p, t - p);
450 if (t >= end) {
451 /* end of fmt string */
452 goto sprint_exit;
453 }
454 p = t + 1; /* skip `%' */
455
456 width = prec = -1;
457 nextvalue = Qundef;
458 retry:
459 switch (*p) {
460 default:
461 if (rb_enc_isprint(*p, enc))
462 rb_raise(rb_eArgError, "malformed format string - %%%c", *p);
463 else
464 rb_raise(rb_eArgError, "malformed format string");
465 break;
466
467 case ' ':
468 CHECK_FOR_FLAGS(flags);
469 flags |= FSPACE;
470 p++;
471 goto retry;
472
473 case '#':
474 CHECK_FOR_FLAGS(flags);
475 flags |= FSHARP;
476 p++;
477 goto retry;
478
479 case '+':
480 CHECK_FOR_FLAGS(flags);
481 flags |= FPLUS;
482 p++;
483 goto retry;
484
485 case '-':
486 CHECK_FOR_FLAGS(flags);
487 flags |= FMINUS;
488 p++;
489 goto retry;
490
491 case '0':
492 CHECK_FOR_FLAGS(flags);
493 flags |= FZERO;
494 p++;
495 goto retry;
496
497 case '1': case '2': case '3': case '4':
498 case '5': case '6': case '7': case '8': case '9':
499 n = 0;
500 GETNUM(n, width);
501 if (*p == '$') {
502 if (nextvalue != Qundef) {
503 rb_raise(rb_eArgError, "value given twice - %d$", n);
504 }
505 nextvalue = GETPOSARG(n);
506 p++;
507 goto retry;
508 }
509 CHECK_FOR_WIDTH(flags);
510 width = n;
511 flags |= FWIDTH;
512 goto retry;
513
514 case '*':
515 CHECK_FOR_WIDTH(flags);
516 flags |= FWIDTH;
517 GETASTER(width);
518 if (width < 0) {
519 flags |= FMINUS;
520 width = -width;
521 }
522 p++;
523 goto retry;
524
525 case '.':
526 if (flags & FPREC0) {
527 rb_raise(rb_eArgError, "precision given twice");
528 }
529 flags |= FPREC|FPREC0;
530
531 prec = 0;
532 p++;
533 if (*p == '*') {
534 GETASTER(prec);
535 if (prec < 0) { /* ignore negative precision */
536 flags &= ~FPREC;
537 }
538 p++;
539 goto retry;
540 }
541
542 GETNUM(prec, precision);
543 goto retry;
544
545 case '\n':
546 case '\0':
547 p--;
548 case '%':
549 if (flags != FNONE) {
550 rb_raise(rb_eArgError, "invalid format character - %%");
551 }
552 PUSH("%", 1);
553 break;
554
555 case 'c':
556 {
557 VALUE val = GETARG();
558 VALUE tmp;
559 int c, n;
560
561 tmp = rb_check_string_type(val);
562 if (!NIL_P(tmp)) {
70fef6e RSTRING_PTR/RSTRING_LEN -> RSTRING_BYTEPTR/RSTRING_BYTELEN, RSTRING_C…
Laurent Sansonetti authored
563 if (rb_enc_strlen(RSTRING_BYTEPTR(tmp),RSTRING_END(tmp),enc) != 1) {
511dc44 initial import
Laurent Sansonetti authored
564 rb_raise(rb_eArgError, "%%c requires a character");
565 }
70fef6e RSTRING_PTR/RSTRING_LEN -> RSTRING_BYTEPTR/RSTRING_BYTELEN, RSTRING_C…
Laurent Sansonetti authored
566 c = rb_enc_codepoint(RSTRING_BYTEPTR(tmp), RSTRING_END(tmp), enc);
511dc44 initial import
Laurent Sansonetti authored
567 }
568 else {
569 c = NUM2INT(val);
570 }
571 n = rb_enc_codelen(c, enc);
572 if (n == 0) {
573 rb_raise(rb_eArgError, "invalid character");
574 }
575 if (!(flags & FWIDTH)) {
576 CHECK(n);
577 rb_enc_mbcput(c, &buf[blen], enc);
578 blen += n;
579 }
580 else if ((flags & FMINUS)) {
581 CHECK(n);
582 rb_enc_mbcput(c, &buf[blen], enc);
583 blen += n;
584 FILL(' ', width-1);
585 }
586 else {
587 FILL(' ', width-1);
588 CHECK(n);
589 rb_enc_mbcput(c, &buf[blen], enc);
590 blen += n;
591 }
592 }
593 break;
594
595 case 's':
596 case 'p':
597 {
598 VALUE arg = GETARG();
599 long len, slen;
600
601 if (*p == 'p') arg = rb_inspect(arg);
602 str = rb_obj_as_string(arg);
603 if (OBJ_TAINTED(str)) tainted = 1;
70fef6e RSTRING_PTR/RSTRING_LEN -> RSTRING_BYTEPTR/RSTRING_BYTELEN, RSTRING_C…
Laurent Sansonetti authored
604 len = RSTRING_BYTELEN(str);
511dc44 initial import
Laurent Sansonetti authored
605 enc = rb_enc_check(result, str);
606 if (flags&(FPREC|FWIDTH)) {
70fef6e RSTRING_PTR/RSTRING_LEN -> RSTRING_BYTEPTR/RSTRING_BYTELEN, RSTRING_C…
Laurent Sansonetti authored
607 slen = rb_enc_strlen(RSTRING_BYTEPTR(str),RSTRING_END(str),enc);
511dc44 initial import
Laurent Sansonetti authored
608 if (slen < 0) {
609 rb_raise(rb_eArgError, "invalid mbstring sequence");
610 }
611 if ((flags&FPREC) && (prec < slen)) {
70fef6e RSTRING_PTR/RSTRING_LEN -> RSTRING_BYTEPTR/RSTRING_BYTELEN, RSTRING_C…
Laurent Sansonetti authored
612 char *p = rb_enc_nth(RSTRING_BYTEPTR(str), RSTRING_END(str),
511dc44 initial import
Laurent Sansonetti authored
613 prec, enc);
614 slen = prec;
70fef6e RSTRING_PTR/RSTRING_LEN -> RSTRING_BYTEPTR/RSTRING_BYTELEN, RSTRING_C…
Laurent Sansonetti authored
615 len = p - RSTRING_BYTEPTR(str);
511dc44 initial import
Laurent Sansonetti authored
616 }
617 /* need to adjust multi-byte string pos */
618 if ((flags&FWIDTH) && (width > slen)) {
619 width -= slen;
620 if (!(flags&FMINUS)) {
621 CHECK(width);
622 while (width--) {
623 buf[blen++] = ' ';
624 }
625 }
626 CHECK(len);
70fef6e RSTRING_PTR/RSTRING_LEN -> RSTRING_BYTEPTR/RSTRING_BYTELEN, RSTRING_C…
Laurent Sansonetti authored
627 memcpy(&buf[blen], RSTRING_BYTEPTR(str), len);
511dc44 initial import
Laurent Sansonetti authored
628 blen += len;
629 if (flags&FMINUS) {
630 CHECK(width);
631 while (width--) {
632 buf[blen++] = ' ';
633 }
634 }
635 rb_enc_associate(result, enc);
636 break;
637 }
638 }
70fef6e RSTRING_PTR/RSTRING_LEN -> RSTRING_BYTEPTR/RSTRING_BYTELEN, RSTRING_C…
Laurent Sansonetti authored
639 PUSH(RSTRING_BYTEPTR(str), len);
511dc44 initial import
Laurent Sansonetti authored
640 rb_enc_associate(result, enc);
641 }
642 break;
643
644 case 'd':
645 case 'i':
646 case 'o':
647 case 'x':
648 case 'X':
649 case 'b':
650 case 'B':
651 case 'u':
652 {
653 volatile VALUE tmp1;
654 volatile VALUE val = GETARG();
655 char fbuf[32], nbuf[64], *s;
656 const char *prefix = 0;
657 int sign = 0, dots = 0;
658 char sc = 0;
659 long v = 0;
660 int base, bignum = 0;
661 int len, pos;
662
663 switch (*p) {
664 case 'd':
665 case 'i':
666 case 'u':
667 sign = 1; break;
668 case 'o':
669 case 'x':
670 case 'X':
671 case 'b':
672 case 'B':
673 if (flags&(FPLUS|FSPACE)) sign = 1;
674 break;
675 }
676 if (flags & FSHARP) {
677 switch (*p) {
678 case 'o':
679 prefix = "0"; break;
680 case 'x':
681 prefix = "0x"; break;
682 case 'X':
683 prefix = "0X"; break;
684 case 'b':
685 prefix = "0b"; break;
686 case 'B':
687 prefix = "0B"; break;
688 }
689 }
690
691 bin_retry:
692 switch (TYPE(val)) {
693 case T_FLOAT:
694 if (FIXABLE((long)RFLOAT_VALUE(val))) {
695 val = LONG2FIX((long)RFLOAT_VALUE(val));
696 goto bin_retry;
697 }
698 val = rb_dbl2big(RFLOAT_VALUE(val));
699 if (FIXNUM_P(val)) goto bin_retry;
700 bignum = 1;
701 break;
702 case T_STRING:
703 val = rb_str_to_inum(val, 0, Qtrue);
704 goto bin_retry;
705 case T_BIGNUM:
706 bignum = 1;
707 break;
708 case T_FIXNUM:
709 v = FIX2LONG(val);
710 break;
711 default:
712 val = rb_Integer(val);
713 goto bin_retry;
714 }
715
716 switch (*p) {
717 case 'o':
718 base = 8; break;
719 case 'x':
720 case 'X':
721 base = 16; break;
722 case 'b':
723 case 'B':
724 base = 2; break;
725 case 'u':
726 case 'd':
727 case 'i':
728 default:
729 base = 10; break;
730 }
731
732 if (!bignum) {
733 if (base == 2) {
734 val = rb_int2big(v);
735 goto bin_retry;
736 }
737 if (sign) {
738 char c = *p;
739 if (c == 'i') c = 'd'; /* %d and %i are identical */
740 if (v < 0) {
741 v = -v;
742 sc = '-';
743 width--;
744 }
745 else if (flags & FPLUS) {
746 sc = '+';
747 width--;
748 }
749 else if (flags & FSPACE) {
750 sc = ' ';
751 width--;
752 }
753 sprintf(fbuf, "%%l%c", c);
754 sprintf(nbuf, fbuf, v);
755 s = nbuf;
756 }
757 else {
758 s = nbuf;
759 if (v < 0) {
760 dots = 1;
761 }
762 sprintf(fbuf, "%%l%c", *p == 'X' ? 'x' : *p);
763 sprintf(++s, fbuf, v);
764 if (v < 0) {
765 char d = 0;
766
767 s = remove_sign_bits(s, base);
768 switch (base) {
769 case 16:
770 d = 'f'; break;
771 case 8:
772 d = '7'; break;
773 }
774 if (d && *s != d) {
775 *--s = d;
776 }
777 }
778 }
779 }
780 else {
781 if (sign) {
782 tmp = rb_big2str(val, base);
70fef6e RSTRING_PTR/RSTRING_LEN -> RSTRING_BYTEPTR/RSTRING_BYTELEN, RSTRING_C…
Laurent Sansonetti authored
783 s = RSTRING_BYTEPTR(tmp);
511dc44 initial import
Laurent Sansonetti authored
784 if (s[0] == '-') {
785 s++;
786 sc = '-';
787 width--;
788 }
789 else if (flags & FPLUS) {
790 sc = '+';
791 width--;
792 }
793 else if (flags & FSPACE) {
794 sc = ' ';
795 width--;
796 }
797 }
798 else {
799 if (!RBIGNUM_SIGN(val)) {
800 val = rb_big_clone(val);
801 rb_big_2comp(val);
802 }
803 tmp1 = tmp = rb_big2str0(val, base, RBIGNUM_SIGN(val));
70fef6e RSTRING_PTR/RSTRING_LEN -> RSTRING_BYTEPTR/RSTRING_BYTELEN, RSTRING_C…
Laurent Sansonetti authored
804 s = RSTRING_BYTEPTR(tmp);
511dc44 initial import
Laurent Sansonetti authored
805 if (*s == '-') {
806 dots = 1;
807 if (base == 10) {
808 rb_warning("negative number for %%u specifier");
809 }
810 s = remove_sign_bits(++s, base);
811 switch (base) {
812 case 16:
813 if (s[0] != 'f') *--s = 'f'; break;
814 case 8:
815 if (s[0] != '7') *--s = '7'; break;
816 case 2:
817 if (s[0] != '1') *--s = '1'; break;
818 }
819 }
820 }
821 }
822
823 pos = -1;
824 len = strlen(s);
825 if (dots) {
826 prec -= 2;
827 width -= 2;
828 }
829
830 if (*p == 'X') {
831 char *pp = s;
832 int c;
833 while ((c = (int)(unsigned char)*pp) != 0) {
834 *pp = rb_enc_toupper(c, enc);
835 pp++;
836 }
837 }
d31a74f synchronized with ruby trunk r15665
Laurent Sansonetti authored
838 if (prefix && !prefix[1]) { /* octal */
511dc44 initial import
Laurent Sansonetti authored
839 if (dots) {
840 prefix = 0;
841 }
842 else if (len == 1 && *s == '0') {
d31a74f synchronized with ruby trunk r15665
Laurent Sansonetti authored
843 len = 0;
844 if (flags & FPREC) prec--;
511dc44 initial import
Laurent Sansonetti authored
845 }
846 else if ((flags & FPREC) && (prec > len)) {
847 prefix = 0;
848 }
849 }
d31a74f synchronized with ruby trunk r15665
Laurent Sansonetti authored
850 else if (len == 1 && *s == '0') {
851 prefix = 0;
852 }
511dc44 initial import
Laurent Sansonetti authored
853 if (prefix) {
854 width -= strlen(prefix);
855 }
856 if ((flags & (FZERO|FMINUS|FPREC)) == FZERO) {
857 prec = width;
858 width = 0;
859 }
860 else {
861 if (prec < len) {
d31a74f synchronized with ruby trunk r15665
Laurent Sansonetti authored
862 if (!prefix && prec == 0 && len == 1 && *s == '0') len = 0;
863 prec = len;
511dc44 initial import
Laurent Sansonetti authored
864 }
865 width -= prec;
866 }
867 if (!(flags&FMINUS)) {
868 CHECK(width);
869 while (width-- > 0) {
870 buf[blen++] = ' ';
871 }
872 }
873 if (sc) PUSH(&sc, 1);
874 if (prefix) {
875 int plen = strlen(prefix);
876 PUSH(prefix, plen);
877 }
878 CHECK(prec - len);
879 if (dots) PUSH("..", 2);
880 if (!bignum && v < 0) {
881 char c = sign_bits(base, p);
882 while (len < prec--) {
883 buf[blen++] = c;
884 }
885 }
886 else if ((flags & (FMINUS|FPREC)) != FMINUS) {
887 char c;
888
889 if (!sign && bignum && !RBIGNUM_SIGN(val))
890 c = sign_bits(base, p);
891 else
892 c = '0';
893 while (len < prec--) {
894 buf[blen++] = c;
895 }
896 }
897 PUSH(s, len);
898 CHECK(width);
899 while (width-- > 0) {
900 buf[blen++] = ' ';
901 }
902 }
903 break;
904
905 case 'f':
906 case 'g':
907 case 'G':
908 case 'e':
909 case 'E':
910 {
911 VALUE val = GETARG();
912 double fval;
913 int i, need = 6;
914 char fbuf[32];
915
916 fval = RFLOAT_VALUE(rb_Float(val));
917 if (isnan(fval) || isinf(fval)) {
918 const char *expr;
919
eee9d7b sync with ruby trunk r15665
Laurent Sansonetti authored
920 if (isnan(fval)) {
511dc44 initial import
Laurent Sansonetti authored
921 expr = "NaN";
922 }
923 else {
924 expr = "Inf";
925 }
926 need = strlen(expr);
927 if ((!isnan(fval) && fval < 0.0) || (flags & FPLUS))
928 need++;
929 if ((flags & FWIDTH) && need < width)
930 need = width;
931
932 CHECK(need);
933 sprintf(&buf[blen], "%*s", need, "");
934 if (flags & FMINUS) {
935 if (!isnan(fval) && fval < 0.0)
936 buf[blen++] = '-';
937 else if (flags & FPLUS)
938 buf[blen++] = '+';
939 else if (flags & FSPACE)
940 blen++;
941 strncpy(&buf[blen], expr, strlen(expr));
942 }
943 else {
944 if (!isnan(fval) && fval < 0.0)
945 buf[blen + need - strlen(expr) - 1] = '-';
946 else if (flags & FPLUS)
947 buf[blen + need - strlen(expr) - 1] = '+';
eee9d7b sync with ruby trunk r15665
Laurent Sansonetti authored
948 else if ((flags & FSPACE) && need > width)
949 blen++;
511dc44 initial import
Laurent Sansonetti authored
950 strncpy(&buf[blen + need - strlen(expr)], expr,
951 strlen(expr));
952 }
953 blen += strlen(&buf[blen]);
954 break;
955 }
956
957 fmt_setup(fbuf, *p, flags, width, prec);
958 need = 0;
959 if (*p != 'e' && *p != 'E') {
960 i = INT_MIN;
961 frexp(fval, &i);
962 if (i > 0)
963 need = BIT_DIGITS(i);
964 }
965 need += (flags&FPREC) ? prec : 6;
966 if ((flags&FWIDTH) && need < width)
967 need = width;
968 need += 20;
969
970 CHECK(need);
971 sprintf(&buf[blen], fbuf, fval);
972 blen += strlen(&buf[blen]);
973 }
974 break;
975 }
976 flags = FNONE;
977 }
978
979 sprint_exit:
980 /* XXX - We cannot validate the number of arguments if (digit)$ style used.
981 */
982 if (posarg >= 0 && nextarg < argc) {
983 const char *mesg = "too many arguments for format string";
984 if (RTEST(ruby_debug)) rb_raise(rb_eArgError, mesg);
985 if (RTEST(ruby_verbose)) rb_warn(mesg);
986 }
987 rb_str_resize(result, blen);
988
989 if (tainted) OBJ_TAINT(result);
990 return result;
991 }
992
993 static void
994 fmt_setup(char *buf, int c, int flags, int width, int prec)
995 {
996 *buf++ = '%';
997 if (flags & FSHARP) *buf++ = '#';
998 if (flags & FPLUS) *buf++ = '+';
999 if (flags & FMINUS) *buf++ = '-';
1000 if (flags & FZERO) *buf++ = '0';
1001 if (flags & FSPACE) *buf++ = ' ';
1002
1003 if (flags & FWIDTH) {
1004 sprintf(buf, "%d", width);
1005 buf += strlen(buf);
1006 }
1007
1008 if (flags & FPREC) {
1009 sprintf(buf, ".%d", prec);
1010 buf += strlen(buf);
1011 }
1012
1013 *buf++ = c;
1014 *buf = '\0';
1015 }
1016
1017 #undef FILE
1018 #define FILE rb_printf_buffer
1019 #define __sbuf rb_printf_sbuf
1020 #define __sFILE rb_printf_sfile
1021 #undef feof
1022 #undef ferror
1023 #undef clearerr
1024 #undef fileno
1025 #if SIZEOF_LONG < SIZEOF_VOIDP
1026 # if SIZEOF_LONG_LONG == SIZEOF_VOIDP
1027 # define _HAVE_SANE_QUAD_
1028 # define _HAVE_LLP64_
1029 # define quad_t LONG_LONG
1030 # define u_quad_t unsigned LONG_LONG
1031 # endif
1032 #endif
1033 #undef vsnprintf
1034 #undef snprintf
1035 #include "missing/vsnprintf.c"
1036
1037 static int
1038 ruby__sfvwrite(register rb_printf_buffer *fp, register struct __suio *uio)
1039 {
1040 struct __siov *iov;
1041 VALUE result = (VALUE)fp->_bf._base;
1042 char *buf = (char*)fp->_p;
1043 size_t len, n;
70fef6e RSTRING_PTR/RSTRING_LEN -> RSTRING_BYTEPTR/RSTRING_BYTELEN, RSTRING_C…
Laurent Sansonetti authored
1044 int blen = buf - RSTRING_BYTEPTR(result), bsiz = fp->_w;
511dc44 initial import
Laurent Sansonetti authored
1045
1046 if (RBASIC(result)->klass) {
1047 rb_raise(rb_eRuntimeError, "rb_vsprintf reentered");
1048 }
1049 if ((len = uio->uio_resid) == 0)
1050 return 0;
1051 CHECK(len);
1052 buf += blen;
1053 fp->_w = bsiz;
1054 for (iov = uio->uio_iov; len > 0; ++iov) {
1055 MEMCPY(buf, iov->iov_base, char, n = iov->iov_len);
1056 buf += n;
1057 len -= n;
1058 }
1059 fp->_p = (unsigned char *)buf;
1060 return 0;
1061 }
26d0e1f merge with ruby trunk r16762 + better/faster objc-like dispatcher
Laurent Sansonetti authored
1062 #endif
511dc44 initial import
Laurent Sansonetti authored
1063
1064 VALUE
1065 rb_enc_vsprintf(rb_encoding *enc, const char *fmt, va_list ap)
1066 {
c2e530d new experimental String implementation, based on CFString (warning: t…
Laurent Sansonetti authored
1067 #if WITH_OBJC
1068 char buffer[512];
1069 int n;
1070 n = vsnprintf(buffer, sizeof buffer, fmt, ap);
1071 return rb_enc_str_new(buffer, n, enc);
1072 #else
511dc44 initial import
Laurent Sansonetti authored
1073 rb_printf_buffer f;
1074 VALUE result;
1075
1076 f._flags = __SWR | __SSTR;
1077 f._bf._size = 0;
1078 f._w = 120;
1079 result = rb_str_buf_new(f._w);
1080 if (enc) rb_enc_associate(result, enc);
1081 f._bf._base = (unsigned char *)result;
70fef6e RSTRING_PTR/RSTRING_LEN -> RSTRING_BYTEPTR/RSTRING_BYTELEN, RSTRING_C…
Laurent Sansonetti authored
1082 f._p = (unsigned char *)RSTRING_BYTEPTR(result);
511dc44 initial import
Laurent Sansonetti authored
1083 RBASIC(result)->klass = 0;
1084 f.vwrite = ruby__sfvwrite;
1085 BSD_vfprintf(&f, fmt, ap);
1086 RBASIC(result)->klass = rb_cString;
70fef6e RSTRING_PTR/RSTRING_LEN -> RSTRING_BYTEPTR/RSTRING_BYTELEN, RSTRING_C…
Laurent Sansonetti authored
1087 rb_str_resize(result, (char *)f._p - RSTRING_BYTEPTR(result));
511dc44 initial import
Laurent Sansonetti authored
1088
1089 return result;
c2e530d new experimental String implementation, based on CFString (warning: t…
Laurent Sansonetti authored
1090 #endif
511dc44 initial import
Laurent Sansonetti authored
1091 }
1092
1093 VALUE
1094 rb_enc_sprintf(rb_encoding *enc, const char *format, ...)
1095 {
1096 VALUE result;
1097 va_list ap;
1098
1099 va_start(ap, format);
1100 result = rb_enc_vsprintf(enc, format, ap);
1101 va_end(ap);
1102
1103 return result;
1104 }
1105
1106 VALUE
1107 rb_vsprintf(const char *fmt, va_list ap)
1108 {
1109 return rb_enc_vsprintf(NULL, fmt, ap);
1110 }
1111
1112 VALUE
1113 rb_sprintf(const char *format, ...)
1114 {
1115 VALUE result;
1116 va_list ap;
1117
1118 va_start(ap, format);
1119 result = rb_vsprintf(format, ap);
1120 va_end(ap);
1121
1122 return result;
1123 }
Something went wrong with that request. Please try again.