Skip to content
This repository
Newer
Older
100644 805 lines (751 sloc) 24.649 kb
9c1d2307 » Laurent Sansonetti
2009-03-11 committing experimental branch content
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 *
4aca1014 » drernie
2010-01-20 Added 2010 Copyrights
6 * Copyright (C) 2007-2010, Apple Inc. All rights reserved.
9c1d2307 » Laurent Sansonetti
2009-03-11 committing experimental branch content
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
cb654164 » Laurent Sansonetti
2009-05-23 the great schism, part I
12 #include <stdarg.h>
13
9c1d2307 » Laurent Sansonetti
2009-03-11 committing experimental branch content
14 #include "ruby/ruby.h"
15 #include "ruby/encoding.h"
4cecb13e » Laurent Sansonetti
2010-03-02 unicode string formats (a work in progress)
16 #include "encoding.h"
9c1d2307 » Laurent Sansonetti
2009-03-11 committing experimental branch content
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) \
77a4b423 » Laurent Sansonetti
2010-02-11 new sprintf implementation (thanks Daniel Cavanagh)
266 ((nth >= argc) ? (rb_raise(rb_eArgError, "too few arguments"), 0) : \
267 argv[nth])
9c1d2307 » Laurent Sansonetti
2009-03-11 committing experimental branch content
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);
4cecb13e » Laurent Sansonetti
2010-03-02 unicode string formats (a work in progress)
295 VALUE result = rb_enc_vsprintf(enc, format, ap);
9c1d2307 » Laurent Sansonetti
2009-03-11 committing experimental branch content
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);
4cecb13e » Laurent Sansonetti
2010-03-02 unicode string formats (a work in progress)
311 VALUE result = rb_vsprintf(format, ap);
9c1d2307 » Laurent Sansonetti
2009-03-11 committing experimental branch content
312 va_end(ap);
313 return result;
314 }
cb654164 » Laurent Sansonetti
2009-05-23 the great schism, part I
315
77a4b423 » Laurent Sansonetti
2010-02-11 new sprintf implementation (thanks Daniel Cavanagh)
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
cb654164 » Laurent Sansonetti
2009-05-23 the great schism, part I
340 static void
87717e75 » Laurent Sansonetti
2010-03-06 some sprintf fixes
341 pad_format_value(VALUE arg, long start, long width, VALUE pad)
cb654164 » Laurent Sansonetti
2009-05-23 the great schism, part I
342 {
87717e75 » Laurent Sansonetti
2010-03-06 some sprintf fixes
343 const long slen = rb_str_chars_len(arg);
77a4b423 » Laurent Sansonetti
2010-02-11 new sprintf implementation (thanks Daniel Cavanagh)
344 if (width <= slen) {
345 return;
346 }
347 if (start < 0) {
348 start += slen + 1;
349 }
350 width -= slen;
351 do {
87717e75 » Laurent Sansonetti
2010-03-06 some sprintf fixes
352 rb_str_update(arg, start, 0, pad);
4cecb13e » Laurent Sansonetti
2010-03-02 unicode string formats (a work in progress)
353 }
354 while (--width > 0);
77a4b423 » Laurent Sansonetti
2010-02-11 new sprintf implementation (thanks Daniel Cavanagh)
355 }
cb654164 » Laurent Sansonetti
2009-05-23 the great schism, part I
356
77a4b423 » Laurent Sansonetti
2010-02-11 new sprintf implementation (thanks Daniel Cavanagh)
357 static long
4cecb13e » Laurent Sansonetti
2010-03-02 unicode string formats (a work in progress)
358 cstr_update(UChar **str, long *str_len, long start, long num, VALUE replace)
77a4b423 » Laurent Sansonetti
2010-02-11 new sprintf implementation (thanks Daniel Cavanagh)
359 {
4cecb13e » Laurent Sansonetti
2010-03-02 unicode string formats (a work in progress)
360 const long len = *str_len;
361 long replace_len = replace == 0 ? 0 : rb_str_chars_len(replace);
77a4b423 » Laurent Sansonetti
2010-02-11 new sprintf implementation (thanks Daniel Cavanagh)
362 if (start + num > len) {
363 num = len - start;
364 }
365 if (replace_len >= num) {
4cecb13e » Laurent Sansonetti
2010-03-02 unicode string formats (a work in progress)
366 *str_len = len + replace_len - num;
367 *str = (UChar *)xrealloc(*str,
368 sizeof(UChar) * (len + replace_len - num));
77a4b423 » Laurent Sansonetti
2010-02-11 new sprintf implementation (thanks Daniel Cavanagh)
369 }
370 if (replace_len != num) {
4cecb13e » Laurent Sansonetti
2010-03-02 unicode string formats (a work in progress)
371 bcopy(*str + start + num, *str + start + replace_len,
372 sizeof(UChar) * (len - start - num));
77a4b423 » Laurent Sansonetti
2010-02-11 new sprintf implementation (thanks Daniel Cavanagh)
373 }
374 if (replace_len > 0) {
4cecb13e » Laurent Sansonetti
2010-03-02 unicode string formats (a work in progress)
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 }
77a4b423 » Laurent Sansonetti
2010-02-11 new sprintf implementation (thanks Daniel Cavanagh)
383 }
384 return replace_len - num;
385 }
386
4cecb13e » Laurent Sansonetti
2010-03-02 unicode string formats (a work in progress)
387 static VALUE
87717e75 » Laurent Sansonetti
2010-03-06 some sprintf fixes
388 get_named_arg(UChar *format_str, long format_len, long *i, VALUE hash)
77a4b423 » Laurent Sansonetti
2010-02-11 new sprintf implementation (thanks Daniel Cavanagh)
389 {
390 if (TYPE(hash) != T_HASH) {
391 rb_raise(rb_eArgError,
392 "hash required for named references");
393 }
4cecb13e » Laurent Sansonetti
2010-03-02 unicode string formats (a work in progress)
394 UChar closing = format_str[(*i)++] + 2;
395 UChar *str_ptr = &format_str[*i];
77a4b423 » Laurent Sansonetti
2010-02-11 new sprintf implementation (thanks Daniel Cavanagh)
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 }
4cecb13e » Laurent Sansonetti
2010-03-02 unicode string formats (a work in progress)
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;
77a4b423 » Laurent Sansonetti
2010-02-11 new sprintf implementation (thanks Daniel Cavanagh)
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);
4cecb13e » Laurent Sansonetti
2010-03-02 unicode string formats (a work in progress)
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;
77a4b423 » Laurent Sansonetti
2010-02-11 new sprintf implementation (thanks Daniel Cavanagh)
429 int j = 0;
430 int ref_type = 0;
87717e75 » Laurent Sansonetti
2010-03-06 some sprintf fixes
431 long format_str_capa = format_len;
77a4b423 » Laurent Sansonetti
2010-02-11 new sprintf implementation (thanks Daniel Cavanagh)
432
87717e75 » Laurent Sansonetti
2010-03-06 some sprintf fixes
433 for (long i = 0; i < format_len; i++) {
77a4b423 » Laurent Sansonetti
2010-02-11 new sprintf implementation (thanks Daniel Cavanagh)
434 if (format_str[i] != '%') {
cb654164 » Laurent Sansonetti
2009-05-23 the great schism, part I
435 continue;
436 }
77a4b423 » Laurent Sansonetti
2010-02-11 new sprintf implementation (thanks Daniel Cavanagh)
437 if (format_str[i + 1] == '%') {
87717e75 » Laurent Sansonetti
2010-03-06 some sprintf fixes
438 cstr_update(&format_str, &format_str_capa, i, 1, 0);
cb654164 » Laurent Sansonetti
2009-05-23 the great schism, part I
439 continue;
440 }
77a4b423 » Laurent Sansonetti
2010-02-11 new sprintf implementation (thanks Daniel Cavanagh)
441
442 bool sharp_flag = false;
443 bool space_flag = false;
444 bool plus_flag = false;
445 bool minus_flag = false;
446 bool zero_flag = false;
447 bool precision_flag = false;
448 bool complete = false;
449 VALUE arg = 0;
450 long width = 0;
451 long precision = 0;
452 int base = 0;
87717e75 » Laurent Sansonetti
2010-03-06 some sprintf fixes
453 VALUE negative_pad = 0;
454 VALUE sharp_pad = rb_str_new2("");
455 const long start = i;
77a4b423 » Laurent Sansonetti
2010-02-11 new sprintf implementation (thanks Daniel Cavanagh)
456
457 while (i++ < format_len) {
cb654164 » Laurent Sansonetti
2009-05-23 the great schism, part I
458 switch (format_str[i]) {
459 case '#':
77a4b423 » Laurent Sansonetti
2010-02-11 new sprintf implementation (thanks Daniel Cavanagh)
460 sharp_flag = true;
cb654164 » Laurent Sansonetti
2009-05-23 the great schism, part I
461 break;
462
463 case '*':
77a4b423 » Laurent Sansonetti
2010-02-11 new sprintf implementation (thanks Daniel Cavanagh)
464 if (format_str[++i] == '<' || format_str[i] == '{') {
465 SET_REF_TYPE(NAMED_REF);
466 width = NUM2LONG(rb_Integer(get_named_arg(format_str,
467 format_len, &i, GETNTHARG(0))));
468 }
469 else {
470 if (isprenum(format_str[i])) {
471 i--;
472 break;
473 }
4cecb13e » Laurent Sansonetti
2010-03-02 unicode string formats (a work in progress)
474
475 num = rb_uchar_strtol(format_str, format_len, i, &pos);
476 if (pos == i--) {
77a4b423 » Laurent Sansonetti
2010-02-11 new sprintf implementation (thanks Daniel Cavanagh)
477 SET_REF_TYPE(REL_REF);
478 width = NUM2LONG(rb_Integer(GETNTHARG(j)));
479 j++;
480 }
4cecb13e » Laurent Sansonetti
2010-03-02 unicode string formats (a work in progress)
481 else if (format_str[pos] == '$') {
77a4b423 » Laurent Sansonetti
2010-02-11 new sprintf implementation (thanks Daniel Cavanagh)
482 SET_REF_TYPE(ABS_REF);
483 width = NUM2LONG(rb_Integer(GETNTHARG(num - 1)));
4cecb13e » Laurent Sansonetti
2010-03-02 unicode string formats (a work in progress)
484 i = pos;
77a4b423 » Laurent Sansonetti
2010-02-11 new sprintf implementation (thanks Daniel Cavanagh)
485 }
486 }
487 if (width < 0) {
488 minus_flag = true;
489 width = -width;
490 }
491 break;
492
493 case ' ':
494 if (!plus_flag) {
495 space_flag = true;
496 }
497 break;
498
499 case '+':
500 plus_flag = true;
501 space_flag = false;
502 break;
503
504 case '-':
505 zero_flag = false;
506 minus_flag = true;
507 break;
508
509 case '0':
510 if (!precision_flag && !minus_flag) {
511 zero_flag = true;
512 }
513 break;
514
515 case '1':
516 case '2':
517 case '3':
518 case '4':
519 case '5':
520 case '6':
521 case '7':
522 case '8':
523 case '9':
4cecb13e » Laurent Sansonetti
2010-03-02 unicode string formats (a work in progress)
524 num = rb_uchar_strtol(format_str, format_len, i, &pos);
525 i = pos;
526 if (format_str[pos] == '$') {
77a4b423 » Laurent Sansonetti
2010-02-11 new sprintf implementation (thanks Daniel Cavanagh)
527 if (num == 0) {
528 rb_raise(rb_eArgError, "invalid absolute argument");
529 }
530 SET_REF_TYPE(ABS_REF);
531 arg = GETNTHARG(num - 1);
532 }
533 else {
534 SET_REF_TYPE(REL_REF);
535 width = num;
536 i--;
537 }
538 break;
539
540 case '.':
541 zero_flag = false;
542 precision_flag = true;
543 if (format_str[++i] == '*') {
544 if (format_str[++i] == '<' || format_str[i] == '{') {
545 SET_REF_TYPE(NAMED_REF);
546 precision = NUM2LONG(rb_Integer(get_named_arg(
547 format_str, format_len, &i, GETNTHARG(0))));
548 }
549 else {
550 if (isprenum(format_str[i])) {
551 i--;
552 break;
553 }
4cecb13e » Laurent Sansonetti
2010-03-02 unicode string formats (a work in progress)
554
555 num = rb_uchar_strtol(format_str, format_len,
556 i, &pos);
557 if (num == i--) {
77a4b423 » Laurent Sansonetti
2010-02-11 new sprintf implementation (thanks Daniel Cavanagh)
558 SET_REF_TYPE(REL_REF);
559 precision = NUM2LONG(rb_Integer(GETNTHARG(j)));
560 j++;
561 }
4cecb13e » Laurent Sansonetti
2010-03-02 unicode string formats (a work in progress)
562 else if (format_str[pos] == '$') {
77a4b423 » Laurent Sansonetti
2010-02-11 new sprintf implementation (thanks Daniel Cavanagh)
563 SET_REF_TYPE(ABS_REF);
564 precision = NUM2LONG(rb_Integer(GETNTHARG(
565 num - 1)));
4cecb13e » Laurent Sansonetti
2010-03-02 unicode string formats (a work in progress)
566 i = pos;
77a4b423 » Laurent Sansonetti
2010-02-11 new sprintf implementation (thanks Daniel Cavanagh)
567 }
568 }
569 }
570 else if (isdigit(format_str[i])) {
4cecb13e » Laurent Sansonetti
2010-03-02 unicode string formats (a work in progress)
571 precision = rb_uchar_strtol(format_str, format_len,
572 i, &pos);
573 i = pos - 1;
77a4b423 » Laurent Sansonetti
2010-02-11 new sprintf implementation (thanks Daniel Cavanagh)
574 }
575 else {
576 rb_raise(rb_eArgError, "invalid precision");
577 }
578
579 if (precision < 0) {
580 precision = 0;
581 }
582 break;
583
584 case '<':
585 case '{':
586 SET_REF_TYPE(NAMED_REF);
587 arg = get_named_arg(format_str, format_len, &i,
588 GETNTHARG(0));
cb654164 » Laurent Sansonetti
2009-05-23 the great schism, part I
589 break;
590
591 case 'd':
77a4b423 » Laurent Sansonetti
2010-02-11 new sprintf implementation (thanks Daniel Cavanagh)
592 case 'D':
cb654164 » Laurent Sansonetti
2009-05-23 the great schism, part I
593 case 'i':
594 case 'u':
77a4b423 » Laurent Sansonetti
2010-02-11 new sprintf implementation (thanks Daniel Cavanagh)
595 case 'U':
596 base = 10;
597 complete = true;
598 break;
599
cb654164 » Laurent Sansonetti
2009-05-23 the great schism, part I
600 case 'x':
601 case 'X':
77a4b423 » Laurent Sansonetti
2010-02-11 new sprintf implementation (thanks Daniel Cavanagh)
602 base = 16;
87717e75 » Laurent Sansonetti
2010-03-06 some sprintf fixes
603 negative_pad = rb_str_new2("f");
604 sharp_pad = rb_str_new2("0x");
77a4b423 » Laurent Sansonetti
2010-02-11 new sprintf implementation (thanks Daniel Cavanagh)
605 complete = true;
cb654164 » Laurent Sansonetti
2009-05-23 the great schism, part I
606 break;
607
77a4b423 » Laurent Sansonetti
2010-02-11 new sprintf implementation (thanks Daniel Cavanagh)
608 case 'o':
609 case 'O':
610 base = 8;
87717e75 » Laurent Sansonetti
2010-03-06 some sprintf fixes
611 negative_pad = rb_str_new2("7");
612 sharp_pad = rb_str_new2("0");
77a4b423 » Laurent Sansonetti
2010-02-11 new sprintf implementation (thanks Daniel Cavanagh)
613 complete = true;
cb654164 » Laurent Sansonetti
2009-05-23 the great schism, part I
614 break;
615
77a4b423 » Laurent Sansonetti
2010-02-11 new sprintf implementation (thanks Daniel Cavanagh)
616 case 'B':
617 case 'b':
618 base = 2;
87717e75 » Laurent Sansonetti
2010-03-06 some sprintf fixes
619 negative_pad = rb_str_new2("1");
620 sharp_pad = rb_str_new2("0b");
77a4b423 » Laurent Sansonetti
2010-02-11 new sprintf implementation (thanks Daniel Cavanagh)
621 complete = true;
cb654164 » Laurent Sansonetti
2009-05-23 the great schism, part I
622 break;
623
77a4b423 » Laurent Sansonetti
2010-02-11 new sprintf implementation (thanks Daniel Cavanagh)
624 case 'c':
625 case 'C':
626 GET_ARG();
627 if (TYPE(arg) == T_STRING) {
628 arg = rb_str_substr(arg, 0, 1);
629 }
630 else {
631 // rb_num_to_chr is broken so leave out the
632 // enc or we don't get range checking
633 arg = rb_num_to_chr(arg, NULL /*rb_enc_get(fmt)*/);
634 }
635 complete = true;
636 break;
637
638 case 'f':
cb654164 » Laurent Sansonetti
2009-05-23 the great schism, part I
639 case 'F':
77a4b423 » Laurent Sansonetti
2010-02-11 new sprintf implementation (thanks Daniel Cavanagh)
640 case 'e':
cb654164 » Laurent Sansonetti
2009-05-23 the great schism, part I
641 case 'E':
77a4b423 » Laurent Sansonetti
2010-02-11 new sprintf implementation (thanks Daniel Cavanagh)
642 case 'g':
cb654164 » Laurent Sansonetti
2009-05-23 the great schism, part I
643 case 'G':
644 case 'a':
645 case 'A':
77a4b423 » Laurent Sansonetti
2010-02-11 new sprintf implementation (thanks Daniel Cavanagh)
646 {
647 // here we construct a new format str and then use
648 // c's sprintf. why? because floats are retarded
649 GET_ARG();
650 double value = RFLOAT_VALUE(rb_Float(arg));
651 complete = true;
652
653 if (isnan(value) || isinf(value)) {
654 arg = rb_str_new2((char *)(isnan(value) ? "NaN" :
655 value < 0 ? "-Inf" : "Inf"));
656 if (isnan(value) || value > 0) {
657 if (plus_flag) {
87717e75 » Laurent Sansonetti
2010-03-06 some sprintf fixes
658 rb_str_update(arg, 0, 0, rb_str_new2("+"));
cb654164 » Laurent Sansonetti
2009-05-23 the great schism, part I
659 }
77a4b423 » Laurent Sansonetti
2010-02-11 new sprintf implementation (thanks Daniel Cavanagh)
660 else if (space_flag) {
87717e75 » Laurent Sansonetti
2010-03-06 some sprintf fixes
661 rb_str_update(arg, 0, 0, rb_str_new2(" "));
cb654164 » Laurent Sansonetti
2009-05-23 the great schism, part I
662 }
663 }
77a4b423 » Laurent Sansonetti
2010-02-11 new sprintf implementation (thanks Daniel Cavanagh)
664 break;
665 }
666
4cecb13e » Laurent Sansonetti
2010-03-02 unicode string formats (a work in progress)
667 arg = rb_unicode_str_new(&format_str[i], 1);
77a4b423 » Laurent Sansonetti
2010-02-11 new sprintf implementation (thanks Daniel Cavanagh)
668 if (precision_flag) {
669 rb_str_update(arg, 0, 0, rb_big2str(LONG2NUM(precision),
670 10));
87717e75 » Laurent Sansonetti
2010-03-06 some sprintf fixes
671 rb_str_update(arg, 0, 0, rb_str_new2("."));
77a4b423 » Laurent Sansonetti
2010-02-11 new sprintf implementation (thanks Daniel Cavanagh)
672 }
673 rb_str_update(arg, 0, 0, rb_big2str(LONG2NUM(width), 10));
674 if (minus_flag) {
87717e75 » Laurent Sansonetti
2010-03-06 some sprintf fixes
675 rb_str_update(arg, 0, 0, rb_str_new2("-"));
77a4b423 » Laurent Sansonetti
2010-02-11 new sprintf implementation (thanks Daniel Cavanagh)
676 }
677 else if (zero_flag) {
87717e75 » Laurent Sansonetti
2010-03-06 some sprintf fixes
678 rb_str_update(arg, 0, 0, rb_str_new2("0"));
77a4b423 » Laurent Sansonetti
2010-02-11 new sprintf implementation (thanks Daniel Cavanagh)
679 }
680 if (plus_flag) {
87717e75 » Laurent Sansonetti
2010-03-06 some sprintf fixes
681 rb_str_update(arg, 0, 0, rb_str_new2("+"));
77a4b423 » Laurent Sansonetti
2010-02-11 new sprintf implementation (thanks Daniel Cavanagh)
682 }
683 else if (space_flag) {
87717e75 » Laurent Sansonetti
2010-03-06 some sprintf fixes
684 rb_str_update(arg, 0, 0, rb_str_new2(" "));
cb654164 » Laurent Sansonetti
2009-05-23 the great schism, part I
685 }
77a4b423 » Laurent Sansonetti
2010-02-11 new sprintf implementation (thanks Daniel Cavanagh)
686 if (sharp_flag) {
87717e75 » Laurent Sansonetti
2010-03-06 some sprintf fixes
687 rb_str_update(arg, 0, 0, rb_str_new2("#"));
77a4b423 » Laurent Sansonetti
2010-02-11 new sprintf implementation (thanks Daniel Cavanagh)
688 }
87717e75 » Laurent Sansonetti
2010-03-06 some sprintf fixes
689 rb_str_update(arg, 0, 0, rb_str_new2("%"));
77a4b423 » Laurent Sansonetti
2010-02-11 new sprintf implementation (thanks Daniel Cavanagh)
690
4cecb13e » Laurent Sansonetti
2010-03-02 unicode string formats (a work in progress)
691 char *ptr;
692 asprintf(&ptr, RSTRING_PTR(arg), value);
693 arg = rb_str_new2(ptr);
694 free(ptr);
cb654164 » Laurent Sansonetti
2009-05-23 the great schism, part I
695 break;
77a4b423 » Laurent Sansonetti
2010-02-11 new sprintf implementation (thanks Daniel Cavanagh)
696 }
cb654164 » Laurent Sansonetti
2009-05-23 the great schism, part I
697
77a4b423 » Laurent Sansonetti
2010-02-11 new sprintf implementation (thanks Daniel Cavanagh)
698 case 's':
699 case 'S':
cb654164 » Laurent Sansonetti
2009-05-23 the great schism, part I
700 case 'p':
701 case '@':
77a4b423 » Laurent Sansonetti
2010-02-11 new sprintf implementation (thanks Daniel Cavanagh)
702 GET_ARG();
87717e75 » Laurent Sansonetti
2010-03-06 some sprintf fixes
703 arg = (tolower(format_str[i]) != 's'
704 ? rb_inspect(arg) : TYPE(arg) == T_STRING
705 ? rb_str_new3(arg) : rb_obj_as_string(arg));
706 if (precision_flag && precision < rb_str_chars_len(arg)) {
77a4b423 » Laurent Sansonetti
2010-02-11 new sprintf implementation (thanks Daniel Cavanagh)
707 CFStringPad((CFMutableStringRef)arg, NULL, precision,
708 0);
cb654164 » Laurent Sansonetti
2009-05-23 the great schism, part I
709 }
77a4b423 » Laurent Sansonetti
2010-02-11 new sprintf implementation (thanks Daniel Cavanagh)
710 complete = true;
cb654164 » Laurent Sansonetti
2009-05-23 the great schism, part I
711 break;
77a4b423 » Laurent Sansonetti
2010-02-11 new sprintf implementation (thanks Daniel Cavanagh)
712
713 default:
714 rb_raise(rb_eArgError, "malformed format string - %%%c",
715 format_str[i]);
716 }
717 if (!complete) {
718 continue;
cb654164 » Laurent Sansonetti
2009-05-23 the great schism, part I
719 }
720
77a4b423 » Laurent Sansonetti
2010-02-11 new sprintf implementation (thanks Daniel Cavanagh)
721 GET_ARG();
cb654164 » Laurent Sansonetti
2009-05-23 the great schism, part I
722
77a4b423 » Laurent Sansonetti
2010-02-11 new sprintf implementation (thanks Daniel Cavanagh)
723 if (base != 0) {
724 bool sign_pad = false;
725 unsigned long num_index = 0;
87717e75 » Laurent Sansonetti
2010-03-06 some sprintf fixes
726 VALUE zero_pad = rb_str_new2("0");
77a4b423 » Laurent Sansonetti
2010-02-11 new sprintf implementation (thanks Daniel Cavanagh)
727
728 VALUE num = rb_Integer(arg);
729 if (TYPE(num) == T_FIXNUM) {
730 num = rb_int2big(FIX2LONG(num));
cb654164 » Laurent Sansonetti
2009-05-23 the great schism, part I
731 }
77a4b423 » Laurent Sansonetti
2010-02-11 new sprintf implementation (thanks Daniel Cavanagh)
732 if (plus_flag || space_flag) {
733 sign_pad = 1;
734 }
735 if (IS_NEG(num)) {
736 num_index = 1;
87717e75 » Laurent Sansonetti
2010-03-06 some sprintf fixes
737 if (!sign_pad && negative_pad != 0) {
77a4b423 » Laurent Sansonetti
2010-02-11 new sprintf implementation (thanks Daniel Cavanagh)
738 zero_pad = negative_pad;
739 num = rb_big_clone(num);
740 rb_big_2comp(num);
741 }
cb654164 » Laurent Sansonetti
2009-05-23 the great schism, part I
742 }
743
77a4b423 » Laurent Sansonetti
2010-02-11 new sprintf implementation (thanks Daniel Cavanagh)
744 arg = rb_big2str(num, base);
87717e75 » Laurent Sansonetti
2010-03-06 some sprintf fixes
745 if (!sign_pad && IS_NEG(num) && negative_pad != 0) {
4cecb13e » Laurent Sansonetti
2010-03-02 unicode string formats (a work in progress)
746 break; // TODO
747 #if 0
748 UChar neg = CFStringGetCharacterAtIndex(negative_pad, 0);
749 char *str_ptr = (char *)RSTRING_PTR(arg) + 1;
77a4b423 » Laurent Sansonetti
2010-02-11 new sprintf implementation (thanks Daniel Cavanagh)
750 if (base == 8) {
751 *str_ptr |= ((~0 << 3) >> ((3 * strlen(str_ptr)) %
752 (sizeof(BDIGIT) * 8))) & ~(~0 << 3);
753 }
754 while (*str_ptr++ == neg) {
755 num_index++;
756 }
757 rb_str_update(arg, 0, num_index, (VALUE)negative_pad);
758 rb_str_update(arg, 0, 0, (VALUE)CFSTR(".."));
759 num_index = 2;
4cecb13e » Laurent Sansonetti
2010-03-02 unicode string formats (a work in progress)
760 #endif
77a4b423 » Laurent Sansonetti
2010-02-11 new sprintf implementation (thanks Daniel Cavanagh)
761 }
cb654164 » Laurent Sansonetti
2009-05-23 the great schism, part I
762
77a4b423 » Laurent Sansonetti
2010-02-11 new sprintf implementation (thanks Daniel Cavanagh)
763 if (precision_flag) {
4cecb13e » Laurent Sansonetti
2010-03-02 unicode string formats (a work in progress)
764 pad_format_value(arg, num_index,
765 precision + (IS_NEG(num)
87717e75 » Laurent Sansonetti
2010-03-06 some sprintf fixes
766 && (sign_pad || negative_pad == 0) ? 1 : 0),
77a4b423 » Laurent Sansonetti
2010-02-11 new sprintf implementation (thanks Daniel Cavanagh)
767 zero_pad);
768 }
e9d7b0c8 » Laurent Sansonetti
2010-02-11 sprintf can now be free of C++ evil
769 if (sharp_flag && rb_cmpint(num, Qfalse, Qfalse) != 0) {
77a4b423 » Laurent Sansonetti
2010-02-11 new sprintf implementation (thanks Daniel Cavanagh)
770 rb_str_update(arg, sign_pad, 0, (VALUE)sharp_pad);
771 num_index += 2;
772 }
773 if (sign_pad && RBIGNUM_POSITIVE_P(num)) {
774 rb_str_update(arg, 0, 0, (VALUE)(plus_flag ?
87717e75 » Laurent Sansonetti
2010-03-06 some sprintf fixes
775 rb_str_new2("+") : rb_str_new2(" ")));
77a4b423 » Laurent Sansonetti
2010-02-11 new sprintf implementation (thanks Daniel Cavanagh)
776 num_index++;
777 }
778 if (zero_flag) {
779 pad_format_value(arg, num_index, width, zero_pad);
780 }
781 if (ISUPPER(format_str[i])) {
782 CFStringUppercase((CFMutableStringRef)arg, NULL);
783 }
784 }
785
786 if (OBJ_TAINTED(arg)) {
787 tainted = true;
788 }
cb654164 » Laurent Sansonetti
2009-05-23 the great schism, part I
789
87717e75 » Laurent Sansonetti
2010-03-06 some sprintf fixes
790 pad_format_value(arg, minus_flag ? -1 : 0, width, rb_str_new2(" "));
791 num = cstr_update(&format_str, &format_str_capa, start,
792 i - start + 1, arg);
793 format_len += num;
77a4b423 » Laurent Sansonetti
2010-02-11 new sprintf implementation (thanks Daniel Cavanagh)
794 i += num;
795 break;
796 }
797 }
cb654164 » Laurent Sansonetti
2009-05-23 the great schism, part I
798
4cecb13e » Laurent Sansonetti
2010-03-02 unicode string formats (a work in progress)
799 bail:
800 fmt = rb_unicode_str_new(format_str, format_len);
801 if (tainted) {
802 OBJ_TAINT(fmt);
803 }
804 return fmt;
cb654164 » Laurent Sansonetti
2009-05-23 the great schism, part I
805 }
Something went wrong with that request. Please try again.