Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 885 lines (828 sloc) 26.506 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) {
abe5fa7 @Watson1978 sprintf("%c") will throw an exception when does not pass one character.
Watson1978 authored
672 if(RSTRING_LEN(arg) != 1) {
673 rb_raise(rb_eArgError, "%%c requires a character");
674 }
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
675 }
676 else {
110e345 fixed some bugs in Numeric#chr
Laurent Sansonetti authored
677 long num = NUM2LONG(arg);
678 if (num < 0 || i > 0xff) {
679 rb_raise(rb_eRangeError, "%ld out of char range",
680 num);
681 }
682 char c = (char)num;
683 arg = rb_str_new(&c, 1);
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
684 }
685 complete = true;
686 break;
687
688 case 'f':
cb65416 the great schism, part I
Laurent Sansonetti authored
689 case 'F':
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
690 case 'e':
cb65416 the great schism, part I
Laurent Sansonetti authored
691 case 'E':
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
692 case 'g':
cb65416 the great schism, part I
Laurent Sansonetti authored
693 case 'G':
694 case 'a':
695 case 'A':
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
696 {
697 // here we construct a new format str and then use
698 // c's sprintf. why? because floats are retarded
699 GET_ARG();
700 double value = RFLOAT_VALUE(rb_Float(arg));
701 complete = true;
702
703 if (isnan(value) || isinf(value)) {
704 arg = rb_str_new2((char *)(isnan(value) ? "NaN" :
705 value < 0 ? "-Inf" : "Inf"));
706 if (isnan(value) || value > 0) {
707 if (plus_flag) {
87717e7 some sprintf fixes
Laurent Sansonetti authored
708 rb_str_update(arg, 0, 0, rb_str_new2("+"));
cb65416 the great schism, part I
Laurent Sansonetti authored
709 }
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
710 else if (space_flag) {
87717e7 some sprintf fixes
Laurent Sansonetti authored
711 rb_str_update(arg, 0, 0, rb_str_new2(" "));
cb65416 the great schism, part I
Laurent Sansonetti authored
712 }
713 }
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
714 break;
715 }
716
4cecb13 unicode string formats (a work in progress)
Laurent Sansonetti authored
717 arg = rb_unicode_str_new(&format_str[i], 1);
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
718 if (precision_flag) {
719 rb_str_update(arg, 0, 0, rb_big2str(LONG2NUM(precision),
720 10));
87717e7 some sprintf fixes
Laurent Sansonetti authored
721 rb_str_update(arg, 0, 0, rb_str_new2("."));
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
722 }
723 rb_str_update(arg, 0, 0, rb_big2str(LONG2NUM(width), 10));
724 if (minus_flag) {
87717e7 some sprintf fixes
Laurent Sansonetti authored
725 rb_str_update(arg, 0, 0, rb_str_new2("-"));
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
726 }
727 else if (zero_flag) {
87717e7 some sprintf fixes
Laurent Sansonetti authored
728 rb_str_update(arg, 0, 0, rb_str_new2("0"));
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
729 }
730 if (plus_flag) {
87717e7 some sprintf fixes
Laurent Sansonetti authored
731 rb_str_update(arg, 0, 0, rb_str_new2("+"));
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
732 }
733 else if (space_flag) {
87717e7 some sprintf fixes
Laurent Sansonetti authored
734 rb_str_update(arg, 0, 0, rb_str_new2(" "));
cb65416 the great schism, part I
Laurent Sansonetti authored
735 }
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
736 if (sharp_flag) {
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 }
87717e7 some sprintf fixes
Laurent Sansonetti authored
739 rb_str_update(arg, 0, 0, rb_str_new2("%"));
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
740
4cecb13 unicode string formats (a work in progress)
Laurent Sansonetti authored
741 char *ptr;
742 asprintf(&ptr, RSTRING_PTR(arg), value);
743 arg = rb_str_new2(ptr);
744 free(ptr);
cb65416 the great schism, part I
Laurent Sansonetti authored
745 break;
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
746 }
cb65416 the great schism, part I
Laurent Sansonetti authored
747
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
748 case 's':
749 case 'S':
cb65416 the great schism, part I
Laurent Sansonetti authored
750 case 'p':
751 case '@':
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
752 GET_ARG();
87717e7 some sprintf fixes
Laurent Sansonetti authored
753 arg = (tolower(format_str[i]) != 's'
754 ? rb_inspect(arg) : TYPE(arg) == T_STRING
755 ? rb_str_new3(arg) : rb_obj_as_string(arg));
d8ba17f @Watson1978 fixed the bug of sprintf() within "%{named}" format.
Watson1978 authored
756 format_s:
87717e7 some sprintf fixes
Laurent Sansonetti authored
757 if (precision_flag && precision < rb_str_chars_len(arg)) {
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
758 CFStringPad((CFMutableStringRef)arg, NULL, precision,
759 0);
cb65416 the great schism, part I
Laurent Sansonetti authored
760 }
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
761 complete = true;
cb65416 the great schism, part I
Laurent Sansonetti authored
762 break;
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
763
b62b438 @Watson1978 modified the style of r4928.
Watson1978 authored
764 case '\n':
765 case '\0':
766 if (format_str[i - 1] == '%') {
421ab55 @Watson1978 sprintf() will not throw exception when passed the format of single % ch...
Watson1978 authored
767 if (format_str[i] == '\n') {
768 arg = rb_str_new("%\n", 2);
769 }
770 else {
b62b438 @Watson1978 modified the style of r4928.
Watson1978 authored
771 if (format_len > i) {
772 arg = rb_str_new("%\0", 2);
773 }
774 else {
775 arg = rb_str_new("%", 1);
776 }
421ab55 @Watson1978 sprintf() will not throw exception when passed the format of single % ch...
Watson1978 authored
777 }
778 complete = true;
b62b438 @Watson1978 modified the style of r4928.
Watson1978 authored
779 break;
421ab55 @Watson1978 sprintf() will not throw exception when passed the format of single % ch...
Watson1978 authored
780 }
b62b438 @Watson1978 modified the style of r4928.
Watson1978 authored
781 rb_raise(rb_eArgError, "malformed format string - %%%c",
782 format_str[i]);
783 break;
784
785 default:
786 rb_raise(rb_eArgError, "malformed format string - %%%c",
787 format_str[i]);
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
788 }
789 if (!complete) {
790 continue;
cb65416 the great schism, part I
Laurent Sansonetti authored
791 }
792
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
793 GET_ARG();
cb65416 the great schism, part I
Laurent Sansonetti authored
794
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
795 if (base != 0) {
796 bool sign_pad = false;
797 unsigned long num_index = 0;
87717e7 some sprintf fixes
Laurent Sansonetti authored
798 VALUE zero_pad = rb_str_new2("0");
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
799
800 VALUE num = rb_Integer(arg);
801 if (TYPE(num) == T_FIXNUM) {
802 num = rb_int2big(FIX2LONG(num));
cb65416 the great schism, part I
Laurent Sansonetti authored
803 }
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
804 if (plus_flag || space_flag) {
805 sign_pad = 1;
806 }
807 if (IS_NEG(num)) {
808 num_index = 1;
87717e7 some sprintf fixes
Laurent Sansonetti authored
809 if (!sign_pad && negative_pad != 0) {
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
810 zero_pad = negative_pad;
811 num = rb_big_clone(num);
812 rb_big_2comp(num);
813 }
cb65416 the great schism, part I
Laurent Sansonetti authored
814 }
815
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
816 arg = rb_big2str(num, base);
87717e7 some sprintf fixes
Laurent Sansonetti authored
817 if (!sign_pad && IS_NEG(num) && negative_pad != 0) {
a539763 fixed a few bugs in #sprintf
Laurent Sansonetti authored
818 UChar neg = rb_str_get_uchar(negative_pad, 0);
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
819 if (base == 8) {
a539763 fixed a few bugs in #sprintf
Laurent Sansonetti authored
820 UChar c = rb_str_get_uchar(arg, 1);
821 const long len = rb_str_chars_len(arg) - 1;
822 c |= ((~0 << 3) >> ((3 * len)
823 % (sizeof(BDIGIT) * 8))) & ~(~0 << 3);
824 rb_str_update(arg, 1, 1, rb_unicode_str_new(&c, 1));
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
825 }
a539763 fixed a few bugs in #sprintf
Laurent Sansonetti authored
826 for (int i = 1, count = rb_str_chars_len(arg); i < count;
827 i++) {
828 if (rb_str_get_uchar(arg, i) != neg) {
829 break;
830 }
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
831 num_index++;
832 }
a539763 fixed a few bugs in #sprintf
Laurent Sansonetti authored
833 rb_str_update(arg, 0, num_index, negative_pad);
834 rb_str_update(arg, 0, 0, rb_str_new2(".."));
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
835 num_index = 2;
836 }
837 if (precision_flag) {
4cecb13 unicode string formats (a work in progress)
Laurent Sansonetti authored
838 pad_format_value(arg, num_index,
839 precision + (IS_NEG(num)
87717e7 some sprintf fixes
Laurent Sansonetti authored
840 && (sign_pad || negative_pad == 0) ? 1 : 0),
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
841 zero_pad);
842 }
e9d7b0c sprintf can now be free of C++ evil
Laurent Sansonetti authored
843 if (sharp_flag && rb_cmpint(num, Qfalse, Qfalse) != 0) {
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
844 rb_str_update(arg, sign_pad, 0, (VALUE)sharp_pad);
845 num_index += 2;
846 }
847 if (sign_pad && RBIGNUM_POSITIVE_P(num)) {
848 rb_str_update(arg, 0, 0, (VALUE)(plus_flag ?
87717e7 some sprintf fixes
Laurent Sansonetti authored
849 rb_str_new2("+") : rb_str_new2(" ")));
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
850 num_index++;
851 }
852 if (zero_flag) {
853 pad_format_value(arg, num_index, width, zero_pad);
854 }
855 if (ISUPPER(format_str[i])) {
856 CFStringUppercase((CFMutableStringRef)arg, NULL);
857 }
858 }
859
860 if (OBJ_TAINTED(arg)) {
861 tainted = true;
862 }
cb65416 the great schism, part I
Laurent Sansonetti authored
863
d6e38f5 #sprintf: don't try to mutate symbols
Laurent Sansonetti authored
864 if (TYPE(arg) == T_SYMBOL) {
865 // Because symbols are not mutable and pad_format_value()
866 // mutates its first argument.
867 arg = rb_sym_to_s(arg);
868 }
87717e7 some sprintf fixes
Laurent Sansonetti authored
869 pad_format_value(arg, minus_flag ? -1 : 0, width, rb_str_new2(" "));
870 num = cstr_update(&format_str, &format_str_capa, start,
871 i - start + 1, arg);
872 format_len += num;
77a4b42 new sprintf implementation (thanks Daniel Cavanagh)
Laurent Sansonetti authored
873 i += num;
874 break;
875 }
876 }
cb65416 the great schism, part I
Laurent Sansonetti authored
877
4cecb13 unicode string formats (a work in progress)
Laurent Sansonetti authored
878 bail:
879 fmt = rb_unicode_str_new(format_str, format_len);
880 if (tainted) {
881 OBJ_TAINT(fmt);
882 }
883 return fmt;
cb65416 the great schism, part I
Laurent Sansonetti authored
884 }
Something went wrong with that request. Please try again.