Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 883 lines (826 sloc) 26.446 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) \
26e8df9 @Watson1978 sprintf() will be given the positional arguments and precision tokens gi...
Watson1978 authored
325 if (arg == 0 && ref_type != 0 && (type) != ref_type) { \
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
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];
18c6303 @Watson1978 fixed the bug of sprintf() within "%<named>" format
Watson1978 authored
396 long length = 0;
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
397 while (*i < format_len && format_str[*i] != closing) {
398 (*i)++;
18c6303 @Watson1978 fixed the bug of sprintf() within "%<named>" format
Watson1978 authored
399 length++;
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
400 }
401 if (*i == format_len) {
402 rb_raise(rb_eArgError,
403 "malformed name - unmatched parenthesis");
404 }
18c6303 @Watson1978 fixed the bug of sprintf() within "%<named>" format
Watson1978 authored
405 VALUE substr = rb_unicode_str_new(str_ptr, (size_t)length);
4cecb13 unicode string formats (a work in progress)
Laurent Sansonetti authored
406 hash = rb_hash_aref(hash, ID2SYM(rb_intern_str(substr)));
407 return hash;
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
408 }
409
410 // XXX look for arguments that are altered but not duped
411 VALUE
412 rb_str_format(int argc, const VALUE *argv, VALUE fmt)
413 {
414 bool tainted = OBJ_TAINTED(fmt);
4cecb13 unicode string formats (a work in progress)
Laurent Sansonetti authored
415
416 UChar *format_str = NULL;
417 long format_len = 0;
418 bool need_free = false;
419 rb_str_get_uchars(fmt, &format_str, &format_len, &need_free);
420 if (format_len == 0) {
421 goto bail;
422 }
423 UChar *tmp = (UChar *)xmalloc(format_len * sizeof(UChar));
424 memcpy(tmp, format_str, format_len * sizeof(UChar));
425 if (need_free) {
426 free(format_str);
427 }
428 format_str = tmp;
429
430 long num, pos;
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
431 int j = 0;
432 int ref_type = 0;
87717e7 some sprintf fixes
Laurent Sansonetti authored
433 long format_str_capa = format_len;
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
434
87717e7 some sprintf fixes
Laurent Sansonetti authored
435 for (long i = 0; i < format_len; i++) {
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
436 if (format_str[i] != '%') {
cb65416 the great schism, part I
Laurent Sansonetti authored
437 continue;
438 }
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
439 if (format_str[i + 1] == '%') {
a539763 fixed a few bugs in #sprintf
Laurent Sansonetti authored
440 num = cstr_update(&format_str, &format_str_capa, i, 1, 0);
441 format_len += num;
cb65416 the great schism, part I
Laurent Sansonetti authored
442 continue;
443 }
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
444
445 bool sharp_flag = false;
446 bool space_flag = false;
447 bool plus_flag = false;
448 bool minus_flag = false;
449 bool zero_flag = false;
42f22c9 @Watson1978 sprintf() will throw an exception when was given width format twice.
Watson1978 authored
450 bool width_flag = false;
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
451 bool precision_flag = false;
9031bde @Watson1978 sprintf() will throw an exception when was given named format twice.
Watson1978 authored
452 bool named_flag = false;
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
453 bool complete = false;
454 VALUE arg = 0;
455 long width = 0;
456 long precision = 0;
457 int base = 0;
87717e7 some sprintf fixes
Laurent Sansonetti authored
458 VALUE negative_pad = 0;
459 VALUE sharp_pad = rb_str_new2("");
460 const long start = i;
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
461
42f22c9 @Watson1978 sprintf() will throw an exception when was given width format twice.
Watson1978 authored
462 #define CHECK_FOR_WIDTH() \
463 if (width_flag) { \
464 rb_raise(rb_eArgError, "width given twice"); \
465 }
466
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
467 while (i++ < format_len) {
cb65416 the great schism, part I
Laurent Sansonetti authored
468 switch (format_str[i]) {
469 case '#':
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
470 sharp_flag = true;
cb65416 the great schism, part I
Laurent Sansonetti authored
471 break;
472
473 case '*':
42f22c9 @Watson1978 sprintf() will throw an exception when was given width format twice.
Watson1978 authored
474 CHECK_FOR_WIDTH();
475 width_flag = true;
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
476 if (format_str[++i] == '<' || format_str[i] == '{') {
477 SET_REF_TYPE(NAMED_REF);
478 width = NUM2LONG(rb_Integer(get_named_arg(format_str,
479 format_len, &i, GETNTHARG(0))));
480 }
481 else {
482 if (isprenum(format_str[i])) {
483 i--;
484 break;
485 }
4cecb13 unicode string formats (a work in progress)
Laurent Sansonetti authored
486
487 num = rb_uchar_strtol(format_str, format_len, i, &pos);
488 if (pos == i--) {
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
489 SET_REF_TYPE(REL_REF);
490 width = NUM2LONG(rb_Integer(GETNTHARG(j)));
491 j++;
492 }
4cecb13 unicode string formats (a work in progress)
Laurent Sansonetti authored
493 else if (format_str[pos] == '$') {
c9853df @Watson1978 String#% will throw an ArgumentError when was given "*0$" format.
Watson1978 authored
494 if (num == 0) {
495 rb_raise(rb_eArgError, "invalid absolute argument");
496 }
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
497 SET_REF_TYPE(ABS_REF);
498 width = NUM2LONG(rb_Integer(GETNTHARG(num - 1)));
4cecb13 unicode string formats (a work in progress)
Laurent Sansonetti authored
499 i = pos;
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
500 }
501 }
502 if (width < 0) {
503 minus_flag = true;
504 width = -width;
505 }
506 break;
507
508 case ' ':
509 if (!plus_flag) {
510 space_flag = true;
511 }
512 break;
513
514 case '+':
515 plus_flag = true;
516 space_flag = false;
517 break;
518
519 case '-':
520 zero_flag = false;
521 minus_flag = true;
522 break;
523
524 case '0':
525 if (!precision_flag && !minus_flag) {
526 zero_flag = true;
527 }
528 break;
529
530 case '1':
531 case '2':
532 case '3':
533 case '4':
534 case '5':
535 case '6':
536 case '7':
537 case '8':
538 case '9':
4cecb13 unicode string formats (a work in progress)
Laurent Sansonetti authored
539 num = rb_uchar_strtol(format_str, format_len, i, &pos);
540 i = pos;
541 if (format_str[pos] == '$') {
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
542 if (num == 0) {
543 rb_raise(rb_eArgError, "invalid absolute argument");
544 }
09f5ee7 @Watson1978 sprintf() will throw an exception when was given positional format twice...
Watson1978 authored
545 if (arg != 0) {
546 rb_raise(rb_eArgError, "value given twice");
547 }
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
548 SET_REF_TYPE(ABS_REF);
549 arg = GETNTHARG(num - 1);
550 }
551 else {
552 SET_REF_TYPE(REL_REF);
553 width = num;
554 i--;
42f22c9 @Watson1978 sprintf() will throw an exception when was given width format twice.
Watson1978 authored
555 CHECK_FOR_WIDTH();
556 width_flag = true;
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
557 }
558 break;
559
560 case '.':
3314262 @Watson1978 sprintf() will throw an exception when was given precision format twice.
Watson1978 authored
561 if (precision_flag) {
562 rb_raise(rb_eArgError, "precision given twice");
563 }
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
564 zero_flag = false;
565 precision_flag = true;
566 if (format_str[++i] == '*') {
567 if (format_str[++i] == '<' || format_str[i] == '{') {
568 SET_REF_TYPE(NAMED_REF);
569 precision = NUM2LONG(rb_Integer(get_named_arg(
570 format_str, format_len, &i, GETNTHARG(0))));
571 }
572 else {
573 if (isprenum(format_str[i])) {
574 i--;
575 break;
576 }
4cecb13 unicode string formats (a work in progress)
Laurent Sansonetti authored
577
578 num = rb_uchar_strtol(format_str, format_len,
579 i, &pos);
225e90f @Watson1978 sprintf() will be given a format which is width's * and precision's *.
Watson1978 authored
580 if (format_str[pos] == '$') {
c9853df @Watson1978 String#% will throw an ArgumentError when was given "*0$" format.
Watson1978 authored
581 if (num == 0) {
582 rb_raise(rb_eArgError, "invalid absolute argument");
583 }
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
584 SET_REF_TYPE(ABS_REF);
585 precision = NUM2LONG(rb_Integer(GETNTHARG(
586 num - 1)));
4cecb13 unicode string formats (a work in progress)
Laurent Sansonetti authored
587 i = pos;
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
588 }
225e90f @Watson1978 sprintf() will be given a format which is width's * and precision's *.
Watson1978 authored
589 else {
590 SET_REF_TYPE(REL_REF);
591 precision = NUM2LONG(rb_Integer(GETNTHARG(j)));
592 j++;
593 i--;
594 }
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
595 }
596 }
597 else if (isdigit(format_str[i])) {
4cecb13 unicode string formats (a work in progress)
Laurent Sansonetti authored
598 precision = rb_uchar_strtol(format_str, format_len,
599 i, &pos);
600 i = pos - 1;
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
601 }
602 else {
603 rb_raise(rb_eArgError, "invalid precision");
604 }
605
606 if (precision < 0) {
607 precision = 0;
608 }
609 break;
610
611 case '<':
612 case '{':
d8ba17f @Watson1978 fixed the bug of sprintf() within "%{named}" format.
Watson1978 authored
613 {
614 char term = (format_str[i] == '<') ? '>' : '}';
615
9031bde @Watson1978 sprintf() will throw an exception when was given named format twice.
Watson1978 authored
616 if (named_flag) {
617 rb_raise(rb_eArgError, "named given twice");
618 }
26e8df9 @Watson1978 sprintf() will be given the positional arguments and precision tokens gi...
Watson1978 authored
619 if (ref_type != 0) {
620 rb_raise(rb_eArgError, "named after numbered");
621 }
9031bde @Watson1978 sprintf() will throw an exception when was given named format twice.
Watson1978 authored
622 named_flag = true;
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
623 SET_REF_TYPE(NAMED_REF);
624 arg = get_named_arg(format_str, format_len, &i,
625 GETNTHARG(0));
d8ba17f @Watson1978 fixed the bug of sprintf() within "%{named}" format.
Watson1978 authored
626 if (term == '}') {
627 if (TYPE(arg) != T_STRING) {
628 arg = rb_obj_as_string(arg);
629 }
630 goto format_s;
631 }
cb65416 the great schism, part I
Laurent Sansonetti authored
632 break;
d8ba17f @Watson1978 fixed the bug of sprintf() within "%{named}" format.
Watson1978 authored
633 }
cb65416 the great schism, part I
Laurent Sansonetti authored
634
635 case 'd':
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
636 case 'D':
cb65416 the great schism, part I
Laurent Sansonetti authored
637 case 'i':
638 case 'u':
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
639 case 'U':
640 base = 10;
641 complete = true;
642 break;
643
cb65416 the great schism, part I
Laurent Sansonetti authored
644 case 'x':
645 case 'X':
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
646 base = 16;
87717e7 some sprintf fixes
Laurent Sansonetti authored
647 negative_pad = rb_str_new2("f");
648 sharp_pad = rb_str_new2("0x");
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
649 complete = true;
cb65416 the great schism, part I
Laurent Sansonetti authored
650 break;
651
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
652 case 'o':
653 case 'O':
654 base = 8;
87717e7 some sprintf fixes
Laurent Sansonetti authored
655 negative_pad = rb_str_new2("7");
656 sharp_pad = rb_str_new2("0");
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
657 complete = true;
cb65416 the great schism, part I
Laurent Sansonetti authored
658 break;
659
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
660 case 'B':
661 case 'b':
662 base = 2;
87717e7 some sprintf fixes
Laurent Sansonetti authored
663 negative_pad = rb_str_new2("1");
664 sharp_pad = rb_str_new2("0b");
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
665 complete = true;
cb65416 the great schism, part I
Laurent Sansonetti authored
666 break;
667
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
668 case 'c':
669 case 'C':
670 GET_ARG();
671 if (TYPE(arg) == T_STRING) {
672 arg = rb_str_substr(arg, 0, 1);
673 }
674 else {
110e345 fixed some bugs in Numeric#chr
Laurent Sansonetti authored
675 long num = NUM2LONG(arg);
676 if (num < 0 || i > 0xff) {
677 rb_raise(rb_eRangeError, "%ld out of char range",
678 num);
679 }
680 char c = (char)num;
681 arg = rb_str_new(&c, 1);
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
682 }
683 complete = true;
684 break;
685
686 case 'f':
cb65416 the great schism, part I
Laurent Sansonetti authored
687 case 'F':
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
688 case 'e':
cb65416 the great schism, part I
Laurent Sansonetti authored
689 case 'E':
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
690 case 'g':
cb65416 the great schism, part I
Laurent Sansonetti authored
691 case 'G':
692 case 'a':
693 case 'A':
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
694 {
695 // here we construct a new format str and then use
696 // c's sprintf. why? because floats are retarded
697 GET_ARG();
698 double value = RFLOAT_VALUE(rb_Float(arg));
699 complete = true;
700
701 if (isnan(value) || isinf(value)) {
702 arg = rb_str_new2((char *)(isnan(value) ? "NaN" :
703 value < 0 ? "-Inf" : "Inf"));
704 if (isnan(value) || value > 0) {
705 if (plus_flag) {
87717e7 some sprintf fixes
Laurent Sansonetti authored
706 rb_str_update(arg, 0, 0, rb_str_new2("+"));
cb65416 the great schism, part I
Laurent Sansonetti authored
707 }
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
708 else if (space_flag) {
87717e7 some sprintf fixes
Laurent Sansonetti authored
709 rb_str_update(arg, 0, 0, rb_str_new2(" "));
cb65416 the great schism, part I
Laurent Sansonetti authored
710 }
711 }
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
712 break;
713 }
714
4cecb13 unicode string formats (a work in progress)
Laurent Sansonetti authored
715 arg = rb_unicode_str_new(&format_str[i], 1);
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
716 if (precision_flag) {
717 rb_str_update(arg, 0, 0, rb_big2str(LONG2NUM(precision),
718 10));
87717e7 some sprintf fixes
Laurent Sansonetti authored
719 rb_str_update(arg, 0, 0, rb_str_new2("."));
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
720 }
721 rb_str_update(arg, 0, 0, rb_big2str(LONG2NUM(width), 10));
722 if (minus_flag) {
87717e7 some sprintf fixes
Laurent Sansonetti authored
723 rb_str_update(arg, 0, 0, rb_str_new2("-"));
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
724 }
725 else if (zero_flag) {
87717e7 some sprintf fixes
Laurent Sansonetti authored
726 rb_str_update(arg, 0, 0, rb_str_new2("0"));
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
727 }
728 if (plus_flag) {
87717e7 some sprintf fixes
Laurent Sansonetti authored
729 rb_str_update(arg, 0, 0, rb_str_new2("+"));
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
730 }
731 else if (space_flag) {
87717e7 some sprintf fixes
Laurent Sansonetti authored
732 rb_str_update(arg, 0, 0, rb_str_new2(" "));
cb65416 the great schism, part I
Laurent Sansonetti authored
733 }
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
734 if (sharp_flag) {
87717e7 some sprintf fixes
Laurent Sansonetti authored
735 rb_str_update(arg, 0, 0, rb_str_new2("#"));
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
736 }
87717e7 some sprintf fixes
Laurent Sansonetti authored
737 rb_str_update(arg, 0, 0, rb_str_new2("%"));
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
738
4cecb13 unicode string formats (a work in progress)
Laurent Sansonetti authored
739 char *ptr;
740 asprintf(&ptr, RSTRING_PTR(arg), value);
741 arg = rb_str_new2(ptr);
742 free(ptr);
cb65416 the great schism, part I
Laurent Sansonetti authored
743 break;
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
744 }
cb65416 the great schism, part I
Laurent Sansonetti authored
745
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
746 case 's':
747 case 'S':
cb65416 the great schism, part I
Laurent Sansonetti authored
748 case 'p':
749 case '@':
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
750 GET_ARG();
87717e7 some sprintf fixes
Laurent Sansonetti authored
751 arg = (tolower(format_str[i]) != 's'
752 ? rb_inspect(arg) : TYPE(arg) == T_STRING
753 ? rb_str_new3(arg) : rb_obj_as_string(arg));
d8ba17f @Watson1978 fixed the bug of sprintf() within "%{named}" format.
Watson1978 authored
754 format_s:
87717e7 some sprintf fixes
Laurent Sansonetti authored
755 if (precision_flag && precision < rb_str_chars_len(arg)) {
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
756 CFStringPad((CFMutableStringRef)arg, NULL, precision,
757 0);
cb65416 the great schism, part I
Laurent Sansonetti authored
758 }
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
759 complete = true;
cb65416 the great schism, part I
Laurent Sansonetti authored
760 break;
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
761
b62b438 @Watson1978 modified the style of r4928.
Watson1978 authored
762 case '\n':
763 case '\0':
764 if (format_str[i - 1] == '%') {
421ab55 @Watson1978 sprintf() will not throw exception when passed the format of single % ch...
Watson1978 authored
765 if (format_str[i] == '\n') {
766 arg = rb_str_new("%\n", 2);
767 }
768 else {
b62b438 @Watson1978 modified the style of r4928.
Watson1978 authored
769 if (format_len > i) {
770 arg = rb_str_new("%\0", 2);
771 }
772 else {
773 arg = rb_str_new("%", 1);
774 }
421ab55 @Watson1978 sprintf() will not throw exception when passed the format of single % ch...
Watson1978 authored
775 }
776 complete = true;
b62b438 @Watson1978 modified the style of r4928.
Watson1978 authored
777 break;
421ab55 @Watson1978 sprintf() will not throw exception when passed the format of single % ch...
Watson1978 authored
778 }
b62b438 @Watson1978 modified the style of r4928.
Watson1978 authored
779 rb_raise(rb_eArgError, "malformed format string - %%%c",
780 format_str[i]);
781 break;
782
783 default:
784 rb_raise(rb_eArgError, "malformed format string - %%%c",
785 format_str[i]);
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
786 }
787 if (!complete) {
788 continue;
cb65416 the great schism, part I
Laurent Sansonetti authored
789 }
790
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
791 GET_ARG();
cb65416 the great schism, part I
Laurent Sansonetti authored
792
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
793 if (base != 0) {
794 bool sign_pad = false;
795 unsigned long num_index = 0;
87717e7 some sprintf fixes
Laurent Sansonetti authored
796 VALUE zero_pad = rb_str_new2("0");
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
797
798 VALUE num = rb_Integer(arg);
799 if (TYPE(num) == T_FIXNUM) {
800 num = rb_int2big(FIX2LONG(num));
cb65416 the great schism, part I
Laurent Sansonetti authored
801 }
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
802 if (plus_flag || space_flag) {
803 sign_pad = 1;
804 }
805 if (IS_NEG(num)) {
806 num_index = 1;
87717e7 some sprintf fixes
Laurent Sansonetti authored
807 if (!sign_pad && negative_pad != 0) {
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
808 zero_pad = negative_pad;
809 num = rb_big_clone(num);
810 rb_big_2comp(num);
811 }
cb65416 the great schism, part I
Laurent Sansonetti authored
812 }
813
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
814 arg = rb_big2str(num, base);
87717e7 some sprintf fixes
Laurent Sansonetti authored
815 if (!sign_pad && IS_NEG(num) && negative_pad != 0) {
a539763 fixed a few bugs in #sprintf
Laurent Sansonetti authored
816 UChar neg = rb_str_get_uchar(negative_pad, 0);
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
817 if (base == 8) {
a539763 fixed a few bugs in #sprintf
Laurent Sansonetti authored
818 UChar c = rb_str_get_uchar(arg, 1);
819 const long len = rb_str_chars_len(arg) - 1;
820 c |= ((~0 << 3) >> ((3 * len)
821 % (sizeof(BDIGIT) * 8))) & ~(~0 << 3);
822 rb_str_update(arg, 1, 1, rb_unicode_str_new(&c, 1));
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
823 }
a539763 fixed a few bugs in #sprintf
Laurent Sansonetti authored
824 for (int i = 1, count = rb_str_chars_len(arg); i < count;
825 i++) {
826 if (rb_str_get_uchar(arg, i) != neg) {
827 break;
828 }
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
829 num_index++;
830 }
a539763 fixed a few bugs in #sprintf
Laurent Sansonetti authored
831 rb_str_update(arg, 0, num_index, negative_pad);
832 rb_str_update(arg, 0, 0, rb_str_new2(".."));
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
833 num_index = 2;
834 }
835 if (precision_flag) {
4cecb13 unicode string formats (a work in progress)
Laurent Sansonetti authored
836 pad_format_value(arg, num_index,
837 precision + (IS_NEG(num)
87717e7 some sprintf fixes
Laurent Sansonetti authored
838 && (sign_pad || negative_pad == 0) ? 1 : 0),
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
839 zero_pad);
840 }
e9d7b0c sprintf can now be free of C++ evil
Laurent Sansonetti authored
841 if (sharp_flag && rb_cmpint(num, Qfalse, Qfalse) != 0) {
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
842 rb_str_update(arg, sign_pad, 0, (VALUE)sharp_pad);
843 num_index += 2;
844 }
845 if (sign_pad && RBIGNUM_POSITIVE_P(num)) {
846 rb_str_update(arg, 0, 0, (VALUE)(plus_flag ?
87717e7 some sprintf fixes
Laurent Sansonetti authored
847 rb_str_new2("+") : rb_str_new2(" ")));
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
848 num_index++;
849 }
850 if (zero_flag) {
851 pad_format_value(arg, num_index, width, zero_pad);
852 }
853 if (ISUPPER(format_str[i])) {
854 CFStringUppercase((CFMutableStringRef)arg, NULL);
855 }
856 }
857
858 if (OBJ_TAINTED(arg)) {
859 tainted = true;
860 }
cb65416 the great schism, part I
Laurent Sansonetti authored
861
d6e38f5 #sprintf: don't try to mutate symbols
Laurent Sansonetti authored
862 if (TYPE(arg) == T_SYMBOL) {
863 // Because symbols are not mutable and pad_format_value()
864 // mutates its first argument.
865 arg = rb_sym_to_s(arg);
866 }
87717e7 some sprintf fixes
Laurent Sansonetti authored
867 pad_format_value(arg, minus_flag ? -1 : 0, width, rb_str_new2(" "));
868 num = cstr_update(&format_str, &format_str_capa, start,
869 i - start + 1, arg);
870 format_len += num;
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
871 i += num;
872 break;
873 }
874 }
cb65416 the great schism, part I
Laurent Sansonetti authored
875
4cecb13 unicode string formats (a work in progress)
Laurent Sansonetti authored
876 bail:
877 fmt = rb_unicode_str_new(format_str, format_len);
878 if (tainted) {
879 OBJ_TAINT(fmt);
880 }
881 return fmt;
cb65416 the great schism, part I
Laurent Sansonetti authored
882 }
Something went wrong with that request. Please try again.