Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 818 lines (764 sloc) 25.011 kb
9c1d230 committing experimental branch content
Laurent Sansonetti authored
1 /*
2 * MacRuby implementation of Ruby 1.9's sprintf.c.
3 *
4 * This file is covered by the Ruby license. See COPYING for more details.
5 *
4aca101 @drernie Added 2010 Copyrights
drernie authored
6 * Copyright (C) 2007-2010, Apple Inc. All rights reserved.
9c1d230 committing experimental branch content
Laurent Sansonetti authored
7 * Copyright (C) 1993-2007 Yukihiro Matsumoto
8 * Copyright (C) 2000 Network Applied Communication Laboratory, Inc.
9 * Copyright (C) 2000 Information-technology Promotion Agency, Japan
10 */
11
cb65416 the great schism, part I
Laurent Sansonetti authored
12 #include <stdarg.h>
13
468a2ea Move Obj-C related headers around.
Thibault Martin-Lagardette authored
14 #include "ruby/macruby.h"
9c1d230 committing experimental branch content
Laurent Sansonetti authored
15 #include "ruby/encoding.h"
4cecb13 unicode string formats (a work in progress)
Laurent Sansonetti authored
16 #include "encoding.h"
9c1d230 committing experimental branch content
Laurent Sansonetti authored
17
18 /*
19 * call-seq:
20 * format(format_string [, arguments...] ) => string
21 * sprintf(format_string [, arguments...] ) => string
22 *
23 * Returns the string resulting from applying <i>format_string</i> to
24 * any additional arguments. Within the format string, any characters
25 * other than format sequences are copied to the result.
26 *
27 * The syntax of a format sequence is follows.
28 *
29 * %[flags][width][.precision]type
30 *
31 * A format
32 * sequence consists of a percent sign, followed by optional flags,
33 * width, and precision indicators, then terminated with a field type
34 * character. The field type controls how the corresponding
35 * <code>sprintf</code> argument is to be interpreted, while the flags
36 * modify that interpretation.
37 *
38 * The field type characters are:
39 *
40 * Field | Integer Format
41 * ------+--------------------------------------------------------------
42 * b | Convert argument as a binary number.
43 * | Negative numbers will be displayed as a two's complement
44 * | prefixed with `..1'.
45 * B | Equivalent to `b', but uses an uppercase 0B for prefix
46 * | in the alternative format by #.
47 * d | Convert argument as a decimal number.
48 * i | Identical to `d'.
49 * o | Convert argument as an octal number.
50 * | Negative numbers will be displayed as a two's complement
51 * | prefixed with `..7'.
52 * u | Identical to `d'.
53 * x | Convert argument as a hexadecimal number.
54 * | Negative numbers will be displayed as a two's complement
55 * | prefixed with `..f' (representing an infinite string of
56 * | leading 'ff's).
57 * X | Equivalent to `x', but uses uppercase letters.
58 *
59 * Field | Float Format
60 * ------+--------------------------------------------------------------
61 * e | Convert floating point argument into exponential notation
62 * | with one digit before the decimal point as [-]d.dddddde[+-]dd.
63 * | The precision specifies the number of digits after the decimal
64 * | point (defaulting to six).
65 * E | Equivalent to `e', but uses an uppercase E to indicate
66 * | the exponent.
67 * f | Convert floating point argument as [-]ddd.dddddd,
68 * | where the precision specifies the number of digits after
69 * | the decimal point.
70 * g | Convert a floating point number using exponential form
71 * | if the exponent is less than -4 or greater than or
72 * | equal to the precision, or in dd.dddd form otherwise.
73 * | The precision specifies the number of significant digits.
74 * G | Equivalent to `g', but use an uppercase `E' in exponent form.
75 *
76 * Field | Other Format
77 * ------+--------------------------------------------------------------
78 * c | Argument is the numeric code for a single character or
79 * | a single character string itself.
80 * p | The valuing of argument.inspect.
81 * s | Argument is a string to be substituted. If the format
82 * | sequence contains a precision, at most that many characters
83 * | will be copied.
84 *
85 * The flags modifies the behavior of the formats.
86 * The flag characters are:
87 *
88 * Flag | Applies to | Meaning
89 * ---------+---------------+-----------------------------------------
90 * space | bBdiouxX | Leave a space at the start of
91 * | eEfgG | non-negative numbers.
92 * | (numeric fmt) | For `o', `x', `X', `b' and `B', use
93 * | | a minus sign with absolute value for
94 * | | negative values.
95 * ---------+---------------+-----------------------------------------
96 * (digit)$ | all | Specifies the absolute argument number
97 * | | for this field. Absolute and relative
98 * | | argument numbers cannot be mixed in a
99 * | | sprintf string.
100 * ---------+---------------+-----------------------------------------
101 * # | bBoxX | Use an alternative format.
102 * | eEfgG | For the conversions `o', increase the precision
103 * | | until the first digit will be `0' if
104 * | | it is not formatted as complements.
105 * | | For the conversions `x', `X', `b' and `B'
106 * | | on non-zero, prefix the result with ``0x'',
107 * | | ``0X'', ``0b'' and ``0B'', respectively.
108 * | | For `e', `E', `f', `g', and 'G',
109 * | | force a decimal point to be added,
110 * | | even if no digits follow.
111 * | | For `g' and 'G', do not remove trailing zeros.
112 * ---------+---------------+-----------------------------------------
113 * + | bBdiouxX | Add a leading plus sign to non-negative
114 * | eEfgG | numbers.
115 * | (numeric fmt) | For `o', `x', `X', `b' and `B', use
116 * | | a minus sign with absolute value for
117 * | | negative values.
118 * ---------+---------------+-----------------------------------------
119 * - | all | Left-justify the result of this conversion.
120 * ---------+---------------+-----------------------------------------
121 * 0 (zero) | bBdiouxX | Pad with zeros, not spaces.
122 * | eEfgG | For `o', `x', `X', `b' and `B', radix-1
123 * | (numeric fmt) | is used for negative numbers formatted as
124 * | | complements.
125 * ---------+---------------+-----------------------------------------
126 * * | all | Use the next argument as the field width.
127 * | | If negative, left-justify the result. If the
128 * | | asterisk is followed by a number and a dollar
129 * | | sign, use the indicated argument as the width.
130 *
131 * Examples of flags:
132 *
133 * # `+' and space flag specifies the sign of non-negative numbers.
134 * sprintf("%d", 123) #=> "123"
135 * sprintf("%+d", 123) #=> "+123"
136 * sprintf("% d", 123) #=> " 123"
137 *
138 * # `#' flag for `o' increases number of digits to show `0'.
139 * # `+' and space flag changes format of negative numbers.
140 * sprintf("%o", 123) #=> "173"
141 * sprintf("%#o", 123) #=> "0173"
142 * sprintf("%+o", -123) #=> "-173"
143 * sprintf("%o", -123) #=> "..7605"
144 * sprintf("%#o", -123) #=> "..7605"
145 *
146 * # `#' flag for `x' add a prefix `0x' for non-zero numbers.
147 * # `+' and space flag disables complements for negative numbers.
148 * sprintf("%x", 123) #=> "7b"
149 * sprintf("%#x", 123) #=> "0x7b"
150 * sprintf("%+x", -123) #=> "-7b"
151 * sprintf("%x", -123) #=> "..f85"
152 * sprintf("%#x", -123) #=> "0x..f85"
153 * sprintf("%#x", 0) #=> "0"
154 *
155 * # `#' for `X' uses the prefix `0X'.
156 * sprintf("%X", 123) #=> "7B"
157 * sprintf("%#X", 123) #=> "0X7B"
158 *
159 * # `#' flag for `b' add a prefix `0b' for non-zero numbers.
160 * # `+' and space flag disables complements for negative numbers.
161 * sprintf("%b", 123) #=> "1111011"
162 * sprintf("%#b", 123) #=> "0b1111011"
163 * sprintf("%+b", -123) #=> "-1111011"
164 * sprintf("%b", -123) #=> "..10000101"
165 * sprintf("%#b", -123) #=> "0b..10000101"
166 * sprintf("%#b", 0) #=> "0"
167 *
168 * # `#' for `B' uses the prefix `0B'.
169 * sprintf("%B", 123) #=> "1111011"
170 * sprintf("%#B", 123) #=> "0B1111011"
171 *
172 * # `#' for `e' forces to show the decimal point.
173 * sprintf("%.0e", 1) #=> "1e+00"
174 * sprintf("%#.0e", 1) #=> "1.e+00"
175 *
176 * # `#' for `f' forces to show the decimal point.
177 * sprintf("%.0f", 1234) #=> "1234"
178 * sprintf("%#.0f", 1234) #=> "1234."
179 *
180 * # `#' for `g' forces to show the decimal point.
181 * # It also disables stripping lowest zeros.
182 * sprintf("%g", 123.4) #=> "123.4"
183 * sprintf("%#g", 123.4) #=> "123.400"
184 * sprintf("%g", 123456) #=> "123456"
185 * sprintf("%#g", 123456) #=> "123456."
186 *
187 * The field width is an optional integer, followed optionally by a
188 * period and a precision. The width specifies the minimum number of
189 * characters that will be written to the result for this field.
190 *
191 * Examples of width:
192 *
193 * # padding is done by spaces, width=20
194 * # 0 or radix-1. <------------------>
195 * sprintf("%20d", 123) #=> " 123"
196 * sprintf("%+20d", 123) #=> " +123"
197 * sprintf("%020d", 123) #=> "00000000000000000123"
198 * sprintf("%+020d", 123) #=> "+0000000000000000123"
199 * sprintf("% 020d", 123) #=> " 0000000000000000123"
200 * sprintf("%-20d", 123) #=> "123 "
201 * sprintf("%-+20d", 123) #=> "+123 "
202 * sprintf("%- 20d", 123) #=> " 123 "
203 * sprintf("%020x", -123) #=> "..ffffffffffffffff85"
204 *
205 * For
206 * numeric fields, the precision controls the number of decimal places
207 * displayed. For string fields, the precision determines the maximum
208 * number of characters to be copied from the string. (Thus, the format
209 * sequence <code>%10.10s</code> will always contribute exactly ten
210 * characters to the result.)
211 *
212 * Examples of precisions:
213 *
214 * # precision for `d', 'o', 'x' and 'b' is
215 * # minimum number of digits <------>
216 * sprintf("%20.8d", 123) #=> " 00000123"
217 * sprintf("%20.8o", 123) #=> " 00000173"
218 * sprintf("%20.8x", 123) #=> " 0000007b"
219 * sprintf("%20.8b", 123) #=> " 01111011"
220 * sprintf("%20.8d", -123) #=> " -00000123"
221 * sprintf("%20.8o", -123) #=> " ..777605"
222 * sprintf("%20.8x", -123) #=> " ..ffff85"
223 * sprintf("%20.8b", -11) #=> " ..110101"
224 *
225 * # "0x" and "0b" for `#x' and `#b' is not counted for
226 * # precision but "0" for `#o' is counted. <------>
227 * sprintf("%#20.8d", 123) #=> " 00000123"
228 * sprintf("%#20.8o", 123) #=> " 00000173"
229 * sprintf("%#20.8x", 123) #=> " 0x0000007b"
230 * sprintf("%#20.8b", 123) #=> " 0b01111011"
231 * sprintf("%#20.8d", -123) #=> " -00000123"
232 * sprintf("%#20.8o", -123) #=> " ..777605"
233 * sprintf("%#20.8x", -123) #=> " 0x..ffff85"
234 * sprintf("%#20.8b", -11) #=> " 0b..110101"
235 *
236 * # precision for `e' is number of
237 * # digits after the decimal point <------>
238 * sprintf("%20.8e", 1234.56789) #=> " 1.23456789e+03"
239 *
240 * # precision for `f' is number of
241 * # digits after the decimal point <------>
242 * sprintf("%20.8f", 1234.56789) #=> " 1234.56789000"
243 *
244 * # precision for `g' is number of
245 * # significant digits <------->
246 * sprintf("%20.8g", 1234.56789) #=> " 1234.5679"
247 *
248 * # <------->
249 * sprintf("%20.8g", 123456789) #=> " 1.2345679e+08"
250 *
251 * # precision for `s' is
252 * # maximum number of characters <------>
253 * sprintf("%20.8s", "string test") #=> " string t"
254 *
255 * Examples:
256 *
257 * sprintf("%d %04x", 123, 123) #=> "123 007b"
258 * sprintf("%08b '%4s'", 123, 123) #=> "01111011 ' 123'"
259 * sprintf("%1$*2$s %2$d %1$s", "hello", 8) #=> " hello 8 hello"
260 * sprintf("%1$*2$s %2$d", "hello", -8) #=> "hello -8"
261 * sprintf("%+g:% g:%-g", 1.23, 1.23, 1.23) #=> "+1.23: 1.23:1.23"
262 * sprintf("%u", -123) #=> "-123"
263 */
264
265 #define GETNTHARG(nth) \
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
266 ((nth >= argc) ? (rb_raise(rb_eArgError, "too few arguments"), 0) : \
267 argv[nth])
9c1d230 committing experimental branch content
Laurent Sansonetti authored
268
269 VALUE
270 rb_f_sprintf_imp(VALUE recv, SEL sel, int argc, VALUE *argv)
271 {
272 return rb_str_format(argc - 1, argv + 1, GETNTHARG(0));
273 }
274
275 VALUE
276 rb_f_sprintf(int argc, const VALUE *argv)
277 {
278 return rb_str_format(argc - 1, argv + 1, GETNTHARG(0));
279 }
280
281 VALUE
282 rb_enc_vsprintf(rb_encoding *enc, const char *fmt, va_list ap)
283 {
284 char buffer[512];
285 int n;
286 n = vsnprintf(buffer, sizeof buffer, fmt, ap);
287 return rb_enc_str_new(buffer, n, enc);
288 }
289
290 VALUE
291 rb_enc_sprintf(rb_encoding *enc, const char *format, ...)
292 {
293 va_list ap;
294 va_start(ap, format);
4cecb13 unicode string formats (a work in progress)
Laurent Sansonetti authored
295 VALUE result = rb_enc_vsprintf(enc, format, ap);
9c1d230 committing experimental branch content
Laurent Sansonetti authored
296 va_end(ap);
297 return result;
298 }
299
300 VALUE
301 rb_vsprintf(const char *fmt, va_list ap)
302 {
303 return rb_enc_vsprintf(NULL, fmt, ap);
304 }
305
306 VALUE
307 rb_sprintf(const char *format, ...)
308 {
309 va_list ap;
310 va_start(ap, format);
4cecb13 unicode string formats (a work in progress)
Laurent Sansonetti authored
311 VALUE result = rb_vsprintf(format, ap);
9c1d230 committing experimental branch content
Laurent Sansonetti authored
312 va_end(ap);
313 return result;
314 }
cb65416 the great schism, part I
Laurent Sansonetti authored
315
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
316 #define IS_NEG(num) RBIGNUM_NEGATIVE_P(num)
317 #define REL_REF 1
318 #define ABS_REF 2
319 #define NAMED_REF 3
320
321 #define REF_NAME(type) \
322 ((type) == REL_REF ? "relative" : (type) == ABS_REF ? "absolute" : "named")
323
324 #define SET_REF_TYPE(type) \
325 if (ref_type != 0 && (type) != ref_type) { \
326 rb_raise(rb_eArgError, "can't mix %s references with %s references", \
327 REF_NAME(type), REF_NAME(ref_type)); \
328 } \
329 ref_type = (type);
330
331 #define GET_ARG() \
332 if (arg == 0) { \
333 SET_REF_TYPE(REL_REF); \
334 arg = GETNTHARG(j); \
335 j++; \
336 }
337
338 #define isprenum(ch) ((ch) == '-' || (ch) == ' ' || (ch) == '+')
339
cb65416 the great schism, part I
Laurent Sansonetti authored
340 static void
87717e7 some sprintf fixes
Laurent Sansonetti authored
341 pad_format_value(VALUE arg, long start, long width, VALUE pad)
cb65416 the great schism, part I
Laurent Sansonetti authored
342 {
87717e7 some sprintf fixes
Laurent Sansonetti authored
343 const long slen = rb_str_chars_len(arg);
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
344 if (width <= slen) {
345 return;
346 }
347 if (start < 0) {
348 start += slen + 1;
349 }
350 width -= slen;
351 do {
87717e7 some sprintf fixes
Laurent Sansonetti authored
352 rb_str_update(arg, start, 0, pad);
4cecb13 unicode string formats (a work in progress)
Laurent Sansonetti authored
353 }
354 while (--width > 0);
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
355 }
cb65416 the great schism, part I
Laurent Sansonetti authored
356
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
357 static long
4cecb13 unicode string formats (a work in progress)
Laurent Sansonetti authored
358 cstr_update(UChar **str, long *str_len, long start, long num, VALUE replace)
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
359 {
4cecb13 unicode string formats (a work in progress)
Laurent Sansonetti authored
360 const long len = *str_len;
361 long replace_len = replace == 0 ? 0 : rb_str_chars_len(replace);
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
362 if (start + num > len) {
363 num = len - start;
364 }
365 if (replace_len >= num) {
4cecb13 unicode string formats (a work in progress)
Laurent Sansonetti authored
366 *str_len = len + replace_len - num;
367 *str = (UChar *)xrealloc(*str,
368 sizeof(UChar) * (len + replace_len - num));
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
369 }
370 if (replace_len != num) {
4cecb13 unicode string formats (a work in progress)
Laurent Sansonetti authored
371 bcopy(*str + start + num, *str + start + replace_len,
372 sizeof(UChar) * (len - start - num));
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
373 }
374 if (replace_len > 0) {
4cecb13 unicode string formats (a work in progress)
Laurent Sansonetti authored
375 UChar *replace_chars = NULL;
376 bool need_free = false;
377 rb_str_get_uchars(replace, &replace_chars, &replace_len, &need_free);
378 assert(replace_len > 0);
379 bcopy(replace_chars, *str + start, sizeof(UChar) * replace_len);
380 if (need_free) {
381 free(replace_chars);
382 }
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
383 }
384 return replace_len - num;
385 }
386
4cecb13 unicode string formats (a work in progress)
Laurent Sansonetti authored
387 static VALUE
87717e7 some sprintf fixes
Laurent Sansonetti authored
388 get_named_arg(UChar *format_str, long format_len, long *i, VALUE hash)
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
389 {
390 if (TYPE(hash) != T_HASH) {
391 rb_raise(rb_eArgError,
392 "hash required for named references");
393 }
4cecb13 unicode string formats (a work in progress)
Laurent Sansonetti authored
394 UChar closing = format_str[(*i)++] + 2;
395 UChar *str_ptr = &format_str[*i];
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
396 while (*i < format_len && format_str[*i] != closing) {
397 (*i)++;
398 }
399 if (*i == format_len) {
400 rb_raise(rb_eArgError,
401 "malformed name - unmatched parenthesis");
402 }
4cecb13 unicode string formats (a work in progress)
Laurent Sansonetti authored
403 VALUE substr = rb_unicode_str_new(str_ptr, str_ptr - format_str);
404 hash = rb_hash_aref(hash, ID2SYM(rb_intern_str(substr)));
405 return hash;
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
406 }
407
408 // XXX look for arguments that are altered but not duped
409 VALUE
410 rb_str_format(int argc, const VALUE *argv, VALUE fmt)
411 {
412 bool tainted = OBJ_TAINTED(fmt);
4cecb13 unicode string formats (a work in progress)
Laurent Sansonetti authored
413
414 UChar *format_str = NULL;
415 long format_len = 0;
416 bool need_free = false;
417 rb_str_get_uchars(fmt, &format_str, &format_len, &need_free);
418 if (format_len == 0) {
419 goto bail;
420 }
421 UChar *tmp = (UChar *)xmalloc(format_len * sizeof(UChar));
422 memcpy(tmp, format_str, format_len * sizeof(UChar));
423 if (need_free) {
424 free(format_str);
425 }
426 format_str = tmp;
427
428 long num, pos;
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
429 int j = 0;
430 int ref_type = 0;
87717e7 some sprintf fixes
Laurent Sansonetti authored
431 long format_str_capa = format_len;
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
432
87717e7 some sprintf fixes
Laurent Sansonetti authored
433 for (long i = 0; i < format_len; i++) {
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
434 if (format_str[i] != '%') {
cb65416 the great schism, part I
Laurent Sansonetti authored
435 continue;
436 }
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
437 if (format_str[i + 1] == '%') {
a539763 fixed a few bugs in #sprintf
Laurent Sansonetti authored
438 num = cstr_update(&format_str, &format_str_capa, i, 1, 0);
439 format_len += num;
cb65416 the great schism, part I
Laurent Sansonetti authored
440 continue;
441 }
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
442
443 bool sharp_flag = false;
444 bool space_flag = false;
445 bool plus_flag = false;
446 bool minus_flag = false;
447 bool zero_flag = false;
448 bool precision_flag = false;
449 bool complete = false;
450 VALUE arg = 0;
451 long width = 0;
452 long precision = 0;
453 int base = 0;
87717e7 some sprintf fixes
Laurent Sansonetti authored
454 VALUE negative_pad = 0;
455 VALUE sharp_pad = rb_str_new2("");
456 const long start = i;
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
457
458 while (i++ < format_len) {
cb65416 the great schism, part I
Laurent Sansonetti authored
459 switch (format_str[i]) {
460 case '#':
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
461 sharp_flag = true;
cb65416 the great schism, part I
Laurent Sansonetti authored
462 break;
463
464 case '*':
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
465 if (format_str[++i] == '<' || format_str[i] == '{') {
466 SET_REF_TYPE(NAMED_REF);
467 width = NUM2LONG(rb_Integer(get_named_arg(format_str,
468 format_len, &i, GETNTHARG(0))));
469 }
470 else {
471 if (isprenum(format_str[i])) {
472 i--;
473 break;
474 }
4cecb13 unicode string formats (a work in progress)
Laurent Sansonetti authored
475
476 num = rb_uchar_strtol(format_str, format_len, i, &pos);
477 if (pos == i--) {
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
478 SET_REF_TYPE(REL_REF);
479 width = NUM2LONG(rb_Integer(GETNTHARG(j)));
480 j++;
481 }
4cecb13 unicode string formats (a work in progress)
Laurent Sansonetti authored
482 else if (format_str[pos] == '$') {
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
483 SET_REF_TYPE(ABS_REF);
484 width = NUM2LONG(rb_Integer(GETNTHARG(num - 1)));
4cecb13 unicode string formats (a work in progress)
Laurent Sansonetti authored
485 i = pos;
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
486 }
487 }
488 if (width < 0) {
489 minus_flag = true;
490 width = -width;
491 }
492 break;
493
494 case ' ':
495 if (!plus_flag) {
496 space_flag = true;
497 }
498 break;
499
500 case '+':
501 plus_flag = true;
502 space_flag = false;
503 break;
504
505 case '-':
506 zero_flag = false;
507 minus_flag = true;
508 break;
509
510 case '0':
511 if (!precision_flag && !minus_flag) {
512 zero_flag = true;
513 }
514 break;
515
516 case '1':
517 case '2':
518 case '3':
519 case '4':
520 case '5':
521 case '6':
522 case '7':
523 case '8':
524 case '9':
4cecb13 unicode string formats (a work in progress)
Laurent Sansonetti authored
525 num = rb_uchar_strtol(format_str, format_len, i, &pos);
526 i = pos;
527 if (format_str[pos] == '$') {
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
528 if (num == 0) {
529 rb_raise(rb_eArgError, "invalid absolute argument");
530 }
531 SET_REF_TYPE(ABS_REF);
532 arg = GETNTHARG(num - 1);
533 }
534 else {
535 SET_REF_TYPE(REL_REF);
536 width = num;
537 i--;
538 }
539 break;
540
541 case '.':
542 zero_flag = false;
543 precision_flag = true;
544 if (format_str[++i] == '*') {
545 if (format_str[++i] == '<' || format_str[i] == '{') {
546 SET_REF_TYPE(NAMED_REF);
547 precision = NUM2LONG(rb_Integer(get_named_arg(
548 format_str, format_len, &i, GETNTHARG(0))));
549 }
550 else {
551 if (isprenum(format_str[i])) {
552 i--;
553 break;
554 }
4cecb13 unicode string formats (a work in progress)
Laurent Sansonetti authored
555
556 num = rb_uchar_strtol(format_str, format_len,
557 i, &pos);
558 if (num == i--) {
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
559 SET_REF_TYPE(REL_REF);
560 precision = NUM2LONG(rb_Integer(GETNTHARG(j)));
561 j++;
562 }
4cecb13 unicode string formats (a work in progress)
Laurent Sansonetti authored
563 else if (format_str[pos] == '$') {
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
564 SET_REF_TYPE(ABS_REF);
565 precision = NUM2LONG(rb_Integer(GETNTHARG(
566 num - 1)));
4cecb13 unicode string formats (a work in progress)
Laurent Sansonetti authored
567 i = pos;
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
568 }
569 }
570 }
571 else if (isdigit(format_str[i])) {
4cecb13 unicode string formats (a work in progress)
Laurent Sansonetti authored
572 precision = rb_uchar_strtol(format_str, format_len,
573 i, &pos);
574 i = pos - 1;
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
575 }
576 else {
577 rb_raise(rb_eArgError, "invalid precision");
578 }
579
580 if (precision < 0) {
581 precision = 0;
582 }
583 break;
584
585 case '<':
586 case '{':
587 SET_REF_TYPE(NAMED_REF);
588 arg = get_named_arg(format_str, format_len, &i,
589 GETNTHARG(0));
cb65416 the great schism, part I
Laurent Sansonetti authored
590 break;
591
592 case 'd':
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
593 case 'D':
cb65416 the great schism, part I
Laurent Sansonetti authored
594 case 'i':
595 case 'u':
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
596 case 'U':
597 base = 10;
598 complete = true;
599 break;
600
cb65416 the great schism, part I
Laurent Sansonetti authored
601 case 'x':
602 case 'X':
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
603 base = 16;
87717e7 some sprintf fixes
Laurent Sansonetti authored
604 negative_pad = rb_str_new2("f");
605 sharp_pad = rb_str_new2("0x");
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
606 complete = true;
cb65416 the great schism, part I
Laurent Sansonetti authored
607 break;
608
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
609 case 'o':
610 case 'O':
611 base = 8;
87717e7 some sprintf fixes
Laurent Sansonetti authored
612 negative_pad = rb_str_new2("7");
613 sharp_pad = rb_str_new2("0");
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
614 complete = true;
cb65416 the great schism, part I
Laurent Sansonetti authored
615 break;
616
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
617 case 'B':
618 case 'b':
619 base = 2;
87717e7 some sprintf fixes
Laurent Sansonetti authored
620 negative_pad = rb_str_new2("1");
621 sharp_pad = rb_str_new2("0b");
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
622 complete = true;
cb65416 the great schism, part I
Laurent Sansonetti authored
623 break;
624
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
625 case 'c':
626 case 'C':
627 GET_ARG();
628 if (TYPE(arg) == T_STRING) {
629 arg = rb_str_substr(arg, 0, 1);
630 }
631 else {
110e345 fixed some bugs in Numeric#chr
Laurent Sansonetti authored
632 long num = NUM2LONG(arg);
633 if (num < 0 || i > 0xff) {
634 rb_raise(rb_eRangeError, "%ld out of char range",
635 num);
636 }
637 char c = (char)num;
638 arg = rb_str_new(&c, 1);
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
639 }
640 complete = true;
641 break;
642
643 case 'f':
cb65416 the great schism, part I
Laurent Sansonetti authored
644 case 'F':
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
645 case 'e':
cb65416 the great schism, part I
Laurent Sansonetti authored
646 case 'E':
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
647 case 'g':
cb65416 the great schism, part I
Laurent Sansonetti authored
648 case 'G':
649 case 'a':
650 case 'A':
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
651 {
652 // here we construct a new format str and then use
653 // c's sprintf. why? because floats are retarded
654 GET_ARG();
655 double value = RFLOAT_VALUE(rb_Float(arg));
656 complete = true;
657
658 if (isnan(value) || isinf(value)) {
659 arg = rb_str_new2((char *)(isnan(value) ? "NaN" :
660 value < 0 ? "-Inf" : "Inf"));
661 if (isnan(value) || value > 0) {
662 if (plus_flag) {
87717e7 some sprintf fixes
Laurent Sansonetti authored
663 rb_str_update(arg, 0, 0, rb_str_new2("+"));
cb65416 the great schism, part I
Laurent Sansonetti authored
664 }
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
665 else if (space_flag) {
87717e7 some sprintf fixes
Laurent Sansonetti authored
666 rb_str_update(arg, 0, 0, rb_str_new2(" "));
cb65416 the great schism, part I
Laurent Sansonetti authored
667 }
668 }
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
669 break;
670 }
671
4cecb13 unicode string formats (a work in progress)
Laurent Sansonetti authored
672 arg = rb_unicode_str_new(&format_str[i], 1);
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
673 if (precision_flag) {
674 rb_str_update(arg, 0, 0, rb_big2str(LONG2NUM(precision),
675 10));
87717e7 some sprintf fixes
Laurent Sansonetti authored
676 rb_str_update(arg, 0, 0, rb_str_new2("."));
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
677 }
678 rb_str_update(arg, 0, 0, rb_big2str(LONG2NUM(width), 10));
679 if (minus_flag) {
87717e7 some sprintf fixes
Laurent Sansonetti authored
680 rb_str_update(arg, 0, 0, rb_str_new2("-"));
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
681 }
682 else if (zero_flag) {
87717e7 some sprintf fixes
Laurent Sansonetti authored
683 rb_str_update(arg, 0, 0, rb_str_new2("0"));
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
684 }
685 if (plus_flag) {
87717e7 some sprintf fixes
Laurent Sansonetti authored
686 rb_str_update(arg, 0, 0, rb_str_new2("+"));
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
687 }
688 else if (space_flag) {
87717e7 some sprintf fixes
Laurent Sansonetti authored
689 rb_str_update(arg, 0, 0, rb_str_new2(" "));
cb65416 the great schism, part I
Laurent Sansonetti authored
690 }
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
691 if (sharp_flag) {
87717e7 some sprintf fixes
Laurent Sansonetti authored
692 rb_str_update(arg, 0, 0, rb_str_new2("#"));
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
693 }
87717e7 some sprintf fixes
Laurent Sansonetti authored
694 rb_str_update(arg, 0, 0, rb_str_new2("%"));
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
695
4cecb13 unicode string formats (a work in progress)
Laurent Sansonetti authored
696 char *ptr;
697 asprintf(&ptr, RSTRING_PTR(arg), value);
698 arg = rb_str_new2(ptr);
699 free(ptr);
cb65416 the great schism, part I
Laurent Sansonetti authored
700 break;
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
701 }
cb65416 the great schism, part I
Laurent Sansonetti authored
702
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
703 case 's':
704 case 'S':
cb65416 the great schism, part I
Laurent Sansonetti authored
705 case 'p':
706 case '@':
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
707 GET_ARG();
87717e7 some sprintf fixes
Laurent Sansonetti authored
708 arg = (tolower(format_str[i]) != 's'
709 ? rb_inspect(arg) : TYPE(arg) == T_STRING
710 ? rb_str_new3(arg) : rb_obj_as_string(arg));
711 if (precision_flag && precision < rb_str_chars_len(arg)) {
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
712 CFStringPad((CFMutableStringRef)arg, NULL, precision,
713 0);
cb65416 the great schism, part I
Laurent Sansonetti authored
714 }
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
715 complete = true;
cb65416 the great schism, part I
Laurent Sansonetti authored
716 break;
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
717
718 default:
719 rb_raise(rb_eArgError, "malformed format string - %%%c",
720 format_str[i]);
721 }
722 if (!complete) {
723 continue;
cb65416 the great schism, part I
Laurent Sansonetti authored
724 }
725
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
726 GET_ARG();
cb65416 the great schism, part I
Laurent Sansonetti authored
727
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
728 if (base != 0) {
729 bool sign_pad = false;
730 unsigned long num_index = 0;
87717e7 some sprintf fixes
Laurent Sansonetti authored
731 VALUE zero_pad = rb_str_new2("0");
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
732
733 VALUE num = rb_Integer(arg);
734 if (TYPE(num) == T_FIXNUM) {
735 num = rb_int2big(FIX2LONG(num));
cb65416 the great schism, part I
Laurent Sansonetti authored
736 }
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
737 if (plus_flag || space_flag) {
738 sign_pad = 1;
739 }
740 if (IS_NEG(num)) {
741 num_index = 1;
87717e7 some sprintf fixes
Laurent Sansonetti authored
742 if (!sign_pad && negative_pad != 0) {
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
743 zero_pad = negative_pad;
744 num = rb_big_clone(num);
745 rb_big_2comp(num);
746 }
cb65416 the great schism, part I
Laurent Sansonetti authored
747 }
748
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
749 arg = rb_big2str(num, base);
87717e7 some sprintf fixes
Laurent Sansonetti authored
750 if (!sign_pad && IS_NEG(num) && negative_pad != 0) {
a539763 fixed a few bugs in #sprintf
Laurent Sansonetti authored
751 UChar neg = rb_str_get_uchar(negative_pad, 0);
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
752 if (base == 8) {
a539763 fixed a few bugs in #sprintf
Laurent Sansonetti authored
753 UChar c = rb_str_get_uchar(arg, 1);
754 const long len = rb_str_chars_len(arg) - 1;
755 c |= ((~0 << 3) >> ((3 * len)
756 % (sizeof(BDIGIT) * 8))) & ~(~0 << 3);
757 rb_str_update(arg, 1, 1, rb_unicode_str_new(&c, 1));
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
758 }
a539763 fixed a few bugs in #sprintf
Laurent Sansonetti authored
759 for (int i = 1, count = rb_str_chars_len(arg); i < count;
760 i++) {
761 if (rb_str_get_uchar(arg, i) != neg) {
762 break;
763 }
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
764 num_index++;
765 }
a539763 fixed a few bugs in #sprintf
Laurent Sansonetti authored
766 rb_str_update(arg, 0, num_index, negative_pad);
767 rb_str_update(arg, 0, 0, rb_str_new2(".."));
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
768 num_index = 2;
769 }
770 if (precision_flag) {
4cecb13 unicode string formats (a work in progress)
Laurent Sansonetti authored
771 pad_format_value(arg, num_index,
772 precision + (IS_NEG(num)
87717e7 some sprintf fixes
Laurent Sansonetti authored
773 && (sign_pad || negative_pad == 0) ? 1 : 0),
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
774 zero_pad);
775 }
e9d7b0c sprintf can now be free of C++ evil
Laurent Sansonetti authored
776 if (sharp_flag && rb_cmpint(num, Qfalse, Qfalse) != 0) {
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
777 rb_str_update(arg, sign_pad, 0, (VALUE)sharp_pad);
778 num_index += 2;
779 }
780 if (sign_pad && RBIGNUM_POSITIVE_P(num)) {
781 rb_str_update(arg, 0, 0, (VALUE)(plus_flag ?
87717e7 some sprintf fixes
Laurent Sansonetti authored
782 rb_str_new2("+") : rb_str_new2(" ")));
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
783 num_index++;
784 }
785 if (zero_flag) {
786 pad_format_value(arg, num_index, width, zero_pad);
787 }
788 if (ISUPPER(format_str[i])) {
789 CFStringUppercase((CFMutableStringRef)arg, NULL);
790 }
791 }
792
793 if (OBJ_TAINTED(arg)) {
794 tainted = true;
795 }
cb65416 the great schism, part I
Laurent Sansonetti authored
796
d6e38f5 #sprintf: don't try to mutate symbols
Laurent Sansonetti authored
797 if (TYPE(arg) == T_SYMBOL) {
798 // Because symbols are not mutable and pad_format_value()
799 // mutates its first argument.
800 arg = rb_sym_to_s(arg);
801 }
87717e7 some sprintf fixes
Laurent Sansonetti authored
802 pad_format_value(arg, minus_flag ? -1 : 0, width, rb_str_new2(" "));
803 num = cstr_update(&format_str, &format_str_capa, start,
804 i - start + 1, arg);
805 format_len += num;
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
806 i += num;
807 break;
808 }
809 }
cb65416 the great schism, part I
Laurent Sansonetti authored
810
4cecb13 unicode string formats (a work in progress)
Laurent Sansonetti authored
811 bail:
812 fmt = rb_unicode_str_new(format_str, format_len);
813 if (tainted) {
814 OBJ_TAINT(fmt);
815 }
816 return fmt;
cb65416 the great schism, part I
Laurent Sansonetti authored
817 }
Something went wrong with that request. Please try again.