Skip to content
Newer
Older
100644 318 lines (304 sloc) 13 KB
9c1d230 committing experimental branch content
Laurent Sansonetti authored Mar 11, 2009
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 *
6 * Copyright (C) 2007-2008, Apple Inc. All rights reserved.
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
12 #include "ruby/ruby.h"
13 #include "ruby/encoding.h"
14 #include <stdarg.h>
15
16 /*
17 * call-seq:
18 * format(format_string [, arguments...] ) => string
19 * sprintf(format_string [, arguments...] ) => string
20 *
21 * Returns the string resulting from applying <i>format_string</i> to
22 * any additional arguments. Within the format string, any characters
23 * other than format sequences are copied to the result.
24 *
25 * The syntax of a format sequence is follows.
26 *
27 * %[flags][width][.precision]type
28 *
29 * A format
30 * sequence consists of a percent sign, followed by optional flags,
31 * width, and precision indicators, then terminated with a field type
32 * character. The field type controls how the corresponding
33 * <code>sprintf</code> argument is to be interpreted, while the flags
34 * modify that interpretation.
35 *
36 * The field type characters are:
37 *
38 * Field | Integer Format
39 * ------+--------------------------------------------------------------
40 * b | Convert argument as a binary number.
41 * | Negative numbers will be displayed as a two's complement
42 * | prefixed with `..1'.
43 * B | Equivalent to `b', but uses an uppercase 0B for prefix
44 * | in the alternative format by #.
45 * d | Convert argument as a decimal number.
46 * i | Identical to `d'.
47 * o | Convert argument as an octal number.
48 * | Negative numbers will be displayed as a two's complement
49 * | prefixed with `..7'.
50 * u | Identical to `d'.
51 * x | Convert argument as a hexadecimal number.
52 * | Negative numbers will be displayed as a two's complement
53 * | prefixed with `..f' (representing an infinite string of
54 * | leading 'ff's).
55 * X | Equivalent to `x', but uses uppercase letters.
56 *
57 * Field | Float Format
58 * ------+--------------------------------------------------------------
59 * e | Convert floating point argument into exponential notation
60 * | with one digit before the decimal point as [-]d.dddddde[+-]dd.
61 * | The precision specifies the number of digits after the decimal
62 * | point (defaulting to six).
63 * E | Equivalent to `e', but uses an uppercase E to indicate
64 * | the exponent.
65 * f | Convert floating point argument as [-]ddd.dddddd,
66 * | where the precision specifies the number of digits after
67 * | the decimal point.
68 * g | Convert a floating point number using exponential form
69 * | if the exponent is less than -4 or greater than or
70 * | equal to the precision, or in dd.dddd form otherwise.
71 * | The precision specifies the number of significant digits.
72 * G | Equivalent to `g', but use an uppercase `E' in exponent form.
73 *
74 * Field | Other Format
75 * ------+--------------------------------------------------------------
76 * c | Argument is the numeric code for a single character or
77 * | a single character string itself.
78 * p | The valuing of argument.inspect.
79 * s | Argument is a string to be substituted. If the format
80 * | sequence contains a precision, at most that many characters
81 * | will be copied.
82 *
83 * The flags modifies the behavior of the formats.
84 * The flag characters are:
85 *
86 * Flag | Applies to | Meaning
87 * ---------+---------------+-----------------------------------------
88 * space | bBdiouxX | Leave a space at the start of
89 * | eEfgG | non-negative numbers.
90 * | (numeric fmt) | For `o', `x', `X', `b' and `B', use
91 * | | a minus sign with absolute value for
92 * | | negative values.
93 * ---------+---------------+-----------------------------------------
94 * (digit)$ | all | Specifies the absolute argument number
95 * | | for this field. Absolute and relative
96 * | | argument numbers cannot be mixed in a
97 * | | sprintf string.
98 * ---------+---------------+-----------------------------------------
99 * # | bBoxX | Use an alternative format.
100 * | eEfgG | For the conversions `o', increase the precision
101 * | | until the first digit will be `0' if
102 * | | it is not formatted as complements.
103 * | | For the conversions `x', `X', `b' and `B'
104 * | | on non-zero, prefix the result with ``0x'',
105 * | | ``0X'', ``0b'' and ``0B'', respectively.
106 * | | For `e', `E', `f', `g', and 'G',
107 * | | force a decimal point to be added,
108 * | | even if no digits follow.
109 * | | For `g' and 'G', do not remove trailing zeros.
110 * ---------+---------------+-----------------------------------------
111 * + | bBdiouxX | Add a leading plus sign to non-negative
112 * | eEfgG | numbers.
113 * | (numeric fmt) | For `o', `x', `X', `b' and `B', use
114 * | | a minus sign with absolute value for
115 * | | negative values.
116 * ---------+---------------+-----------------------------------------
117 * - | all | Left-justify the result of this conversion.
118 * ---------+---------------+-----------------------------------------
119 * 0 (zero) | bBdiouxX | Pad with zeros, not spaces.
120 * | eEfgG | For `o', `x', `X', `b' and `B', radix-1
121 * | (numeric fmt) | is used for negative numbers formatted as
122 * | | complements.
123 * ---------+---------------+-----------------------------------------
124 * * | all | Use the next argument as the field width.
125 * | | If negative, left-justify the result. If the
126 * | | asterisk is followed by a number and a dollar
127 * | | sign, use the indicated argument as the width.
128 *
129 * Examples of flags:
130 *
131 * # `+' and space flag specifies the sign of non-negative numbers.
132 * sprintf("%d", 123) #=> "123"
133 * sprintf("%+d", 123) #=> "+123"
134 * sprintf("% d", 123) #=> " 123"
135 *
136 * # `#' flag for `o' increases number of digits to show `0'.
137 * # `+' and space flag changes format of negative numbers.
138 * sprintf("%o", 123) #=> "173"
139 * sprintf("%#o", 123) #=> "0173"
140 * sprintf("%+o", -123) #=> "-173"
141 * sprintf("%o", -123) #=> "..7605"
142 * sprintf("%#o", -123) #=> "..7605"
143 *
144 * # `#' flag for `x' add a prefix `0x' for non-zero numbers.
145 * # `+' and space flag disables complements for negative numbers.
146 * sprintf("%x", 123) #=> "7b"
147 * sprintf("%#x", 123) #=> "0x7b"
148 * sprintf("%+x", -123) #=> "-7b"
149 * sprintf("%x", -123) #=> "..f85"
150 * sprintf("%#x", -123) #=> "0x..f85"
151 * sprintf("%#x", 0) #=> "0"
152 *
153 * # `#' for `X' uses the prefix `0X'.
154 * sprintf("%X", 123) #=> "7B"
155 * sprintf("%#X", 123) #=> "0X7B"
156 *
157 * # `#' flag for `b' add a prefix `0b' for non-zero numbers.
158 * # `+' and space flag disables complements for negative numbers.
159 * sprintf("%b", 123) #=> "1111011"
160 * sprintf("%#b", 123) #=> "0b1111011"
161 * sprintf("%+b", -123) #=> "-1111011"
162 * sprintf("%b", -123) #=> "..10000101"
163 * sprintf("%#b", -123) #=> "0b..10000101"
164 * sprintf("%#b", 0) #=> "0"
165 *
166 * # `#' for `B' uses the prefix `0B'.
167 * sprintf("%B", 123) #=> "1111011"
168 * sprintf("%#B", 123) #=> "0B1111011"
169 *
170 * # `#' for `e' forces to show the decimal point.
171 * sprintf("%.0e", 1) #=> "1e+00"
172 * sprintf("%#.0e", 1) #=> "1.e+00"
173 *
174 * # `#' for `f' forces to show the decimal point.
175 * sprintf("%.0f", 1234) #=> "1234"
176 * sprintf("%#.0f", 1234) #=> "1234."
177 *
178 * # `#' for `g' forces to show the decimal point.
179 * # It also disables stripping lowest zeros.
180 * sprintf("%g", 123.4) #=> "123.4"
181 * sprintf("%#g", 123.4) #=> "123.400"
182 * sprintf("%g", 123456) #=> "123456"
183 * sprintf("%#g", 123456) #=> "123456."
184 *
185 * The field width is an optional integer, followed optionally by a
186 * period and a precision. The width specifies the minimum number of
187 * characters that will be written to the result for this field.
188 *
189 * Examples of width:
190 *
191 * # padding is done by spaces, width=20
192 * # 0 or radix-1. <------------------>
193 * sprintf("%20d", 123) #=> " 123"
194 * sprintf("%+20d", 123) #=> " +123"
195 * sprintf("%020d", 123) #=> "00000000000000000123"
196 * sprintf("%+020d", 123) #=> "+0000000000000000123"
197 * sprintf("% 020d", 123) #=> " 0000000000000000123"
198 * sprintf("%-20d", 123) #=> "123 "
199 * sprintf("%-+20d", 123) #=> "+123 "
200 * sprintf("%- 20d", 123) #=> " 123 "
201 * sprintf("%020x", -123) #=> "..ffffffffffffffff85"
202 *
203 * For
204 * numeric fields, the precision controls the number of decimal places
205 * displayed. For string fields, the precision determines the maximum
206 * number of characters to be copied from the string. (Thus, the format
207 * sequence <code>%10.10s</code> will always contribute exactly ten
208 * characters to the result.)
209 *
210 * Examples of precisions:
211 *
212 * # precision for `d', 'o', 'x' and 'b' is
213 * # minimum number of digits <------>
214 * sprintf("%20.8d", 123) #=> " 00000123"
215 * sprintf("%20.8o", 123) #=> " 00000173"
216 * sprintf("%20.8x", 123) #=> " 0000007b"
217 * sprintf("%20.8b", 123) #=> " 01111011"
218 * sprintf("%20.8d", -123) #=> " -00000123"
219 * sprintf("%20.8o", -123) #=> " ..777605"
220 * sprintf("%20.8x", -123) #=> " ..ffff85"
221 * sprintf("%20.8b", -11) #=> " ..110101"
222 *
223 * # "0x" and "0b" for `#x' and `#b' is not counted for
224 * # precision but "0" for `#o' is counted. <------>
225 * sprintf("%#20.8d", 123) #=> " 00000123"
226 * sprintf("%#20.8o", 123) #=> " 00000173"
227 * sprintf("%#20.8x", 123) #=> " 0x0000007b"
228 * sprintf("%#20.8b", 123) #=> " 0b01111011"
229 * sprintf("%#20.8d", -123) #=> " -00000123"
230 * sprintf("%#20.8o", -123) #=> " ..777605"
231 * sprintf("%#20.8x", -123) #=> " 0x..ffff85"
232 * sprintf("%#20.8b", -11) #=> " 0b..110101"
233 *
234 * # precision for `e' is number of
235 * # digits after the decimal point <------>
236 * sprintf("%20.8e", 1234.56789) #=> " 1.23456789e+03"
237 *
238 * # precision for `f' is number of
239 * # digits after the decimal point <------>
240 * sprintf("%20.8f", 1234.56789) #=> " 1234.56789000"
241 *
242 * # precision for `g' is number of
243 * # significant digits <------->
244 * sprintf("%20.8g", 1234.56789) #=> " 1234.5679"
245 *
246 * # <------->
247 * sprintf("%20.8g", 123456789) #=> " 1.2345679e+08"
248 *
249 * # precision for `s' is
250 * # maximum number of characters <------>
251 * sprintf("%20.8s", "string test") #=> " string t"
252 *
253 * Examples:
254 *
255 * sprintf("%d %04x", 123, 123) #=> "123 007b"
256 * sprintf("%08b '%4s'", 123, 123) #=> "01111011 ' 123'"
257 * sprintf("%1$*2$s %2$d %1$s", "hello", 8) #=> " hello 8 hello"
258 * sprintf("%1$*2$s %2$d", "hello", -8) #=> "hello -8"
259 * sprintf("%+g:% g:%-g", 1.23, 1.23, 1.23) #=> "+1.23: 1.23:1.23"
260 * sprintf("%u", -123) #=> "-123"
261 */
262
263 #define GETNTHARG(nth) \
264 ((nth >= argc) ? (rb_raise(rb_eArgError, "too few arguments"), 0) : argv[nth])
265
266 VALUE
267 rb_f_sprintf_imp(VALUE recv, SEL sel, int argc, VALUE *argv)
268 {
269 return rb_str_format(argc - 1, argv + 1, GETNTHARG(0));
270 }
271
272 VALUE
273 rb_f_sprintf(int argc, const VALUE *argv)
274 {
275 return rb_str_format(argc - 1, argv + 1, GETNTHARG(0));
276 }
277
278 VALUE
279 rb_enc_vsprintf(rb_encoding *enc, const char *fmt, va_list ap)
280 {
281 char buffer[512];
282 int n;
283 n = vsnprintf(buffer, sizeof buffer, fmt, ap);
284 return rb_enc_str_new(buffer, n, enc);
285 }
286
287 VALUE
288 rb_enc_sprintf(rb_encoding *enc, const char *format, ...)
289 {
290 VALUE result;
291 va_list ap;
292
293 va_start(ap, format);
294 result = rb_enc_vsprintf(enc, format, ap);
295 va_end(ap);
296
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 VALUE result;
310 va_list ap;
311
312 va_start(ap, format);
313 result = rb_vsprintf(format, ap);
314 va_end(ap);
315
316 return result;
317 }
Something went wrong with that request. Please try again.