source: EcnlProtoTool/trunk/mruby-1.3.0/mrbgems/mruby-sprintf/src/sprintf.c@ 331

Last change on this file since 331 was 331, checked in by coas-nagasima, 6 years ago

prototoolに関連するプロジェクトをnewlibからmuslを使うよう変更・更新
ntshellをnewlibの下位の実装から、muslのsyscallの実装に変更・更新
以下のOSSをアップデート
・mruby-1.3.0
・musl-1.1.18
・onigmo-6.1.3
・tcc-0.9.27
以下のOSSを追加
・openssl-1.1.0e
・curl-7.57.0
・zlib-1.2.11
以下のmrbgemsを追加
・iij/mruby-digest
・iij/mruby-env
・iij/mruby-errno
・iij/mruby-iijson
・iij/mruby-ipaddr
・iij/mruby-mock
・iij/mruby-require
・iij/mruby-tls-openssl

  • Property svn:eol-style set to native
  • Property svn:mime-type set to text/x-csrc;charset=UTF-8
File size: 32.7 KB
Line 
1/*
2** sprintf.c - Kernel.#sprintf
3**
4** See Copyright Notice in mruby.h
5*/
6
7#include <mruby.h>
8
9#include <limits.h>
10#include <stdio.h>
11#include <string.h>
12#include <mruby/string.h>
13#include <mruby/hash.h>
14#include <mruby/numeric.h>
15#include <math.h>
16#include <ctype.h>
17
18#define BIT_DIGITS(N) (((N)*146)/485 + 1) /* log2(10) =~ 146/485 */
19#define BITSPERDIG MRB_INT_BIT
20#define EXTENDSIGN(n, l) (((~0U << (n)) >> (((n)*(l)) % BITSPERDIG)) & ~(~0U << (n)))
21
22mrb_value mrb_str_format(mrb_state *, int, const mrb_value *, mrb_value);
23static void fmt_setup(char*,size_t,int,int,mrb_int,mrb_int);
24
25static char*
26remove_sign_bits(char *str, int base)
27{
28 char *t;
29
30 t = str;
31 if (base == 16) {
32 while (*t == 'f') {
33 t++;
34 }
35 }
36 else if (base == 8) {
37 *t |= EXTENDSIGN(3, strlen(t));
38 while (*t == '7') {
39 t++;
40 }
41 }
42 else if (base == 2) {
43 while (*t == '1') {
44 t++;
45 }
46 }
47
48 return t;
49}
50
51static char
52sign_bits(int base, const char *p)
53{
54 char c;
55
56 switch (base) {
57 case 16:
58 if (*p == 'X') c = 'F';
59 else c = 'f';
60 break;
61 case 8:
62 c = '7'; break;
63 case 2:
64 c = '1'; break;
65 default:
66 c = '.'; break;
67 }
68 return c;
69}
70
71static mrb_value
72mrb_fix2binstr(mrb_state *mrb, mrb_value x, int base)
73{
74 char buf[66], *b = buf + sizeof buf;
75 mrb_int num = mrb_fixnum(x);
76 uint64_t val = (uint64_t)num;
77 char d;
78
79 if (base != 2) {
80 mrb_raisef(mrb, E_ARGUMENT_ERROR, "invalid radix %S", mrb_fixnum_value(base));
81 }
82 if (val == 0) {
83 return mrb_str_new_lit(mrb, "0");
84 }
85 *--b = '\0';
86 do {
87 *--b = mrb_digitmap[(int)(val % base)];
88 } while (val /= base);
89
90 if (num < 0) {
91 b = remove_sign_bits(b, base);
92 switch (base) {
93 case 16: d = 'f'; break;
94 case 8: d = '7'; break;
95 case 2: d = '1'; break;
96 default: d = 0; break;
97 }
98
99 if (d && *b != d) {
100 *--b = d;
101 }
102 }
103
104 return mrb_str_new_cstr(mrb, b);
105}
106
107#define FNONE 0
108#define FSHARP 1
109#define FMINUS 2
110#define FPLUS 4
111#define FZERO 8
112#define FSPACE 16
113#define FWIDTH 32
114#define FPREC 64
115#define FPREC0 128
116
117#define CHECK(l) do {\
118/* int cr = ENC_CODERANGE(result);*/\
119 while ((l) >= bsiz - blen) {\
120 bsiz*=2;\
121 if (bsiz < 0) mrb_raise(mrb, E_ARGUMENT_ERROR, "too big specifier"); \
122 }\
123 mrb_str_resize(mrb, result, bsiz);\
124/* ENC_CODERANGE_SET(result, cr);*/\
125 buf = RSTRING_PTR(result);\
126} while (0)
127
128#define PUSH(s, l) do { \
129 CHECK(l);\
130 memcpy(&buf[blen], s, l);\
131 blen += (l);\
132} while (0)
133
134#define FILL(c, l) do { \
135 CHECK(l);\
136 memset(&buf[blen], c, l);\
137 blen += (l);\
138} while (0)
139
140static void
141check_next_arg(mrb_state *mrb, int posarg, int nextarg)
142{
143 switch (posarg) {
144 case -1:
145 mrb_raisef(mrb, E_ARGUMENT_ERROR, "unnumbered(%S) mixed with numbered", mrb_fixnum_value(nextarg));
146 break;
147 case -2:
148 mrb_raisef(mrb, E_ARGUMENT_ERROR, "unnumbered(%S) mixed with named", mrb_fixnum_value(nextarg));
149 break;
150 default:
151 break;
152 }
153}
154
155static void
156check_pos_arg(mrb_state *mrb, int posarg, int n)
157{
158 if (posarg > 0) {
159 mrb_raisef(mrb, E_ARGUMENT_ERROR, "numbered(%S) after unnumbered(%S)",
160 mrb_fixnum_value(n), mrb_fixnum_value(posarg));
161 }
162 if (posarg == -2) {
163 mrb_raisef(mrb, E_ARGUMENT_ERROR, "numbered(%S) after named", mrb_fixnum_value(n));
164 }
165 if (n < 1) {
166 mrb_raisef(mrb, E_ARGUMENT_ERROR, "invalid index - %S$", mrb_fixnum_value(n));
167 }
168}
169
170static void
171check_name_arg(mrb_state *mrb, int posarg, const char *name, int len)
172{
173 if (posarg > 0) {
174 mrb_raisef(mrb, E_ARGUMENT_ERROR, "named%S after unnumbered(%S)",
175 mrb_str_new(mrb, (name), (len)), mrb_fixnum_value(posarg));
176 }
177 if (posarg == -1) {
178 mrb_raisef(mrb, E_ARGUMENT_ERROR, "named%S after numbered", mrb_str_new(mrb, (name), (len)));
179 }
180}
181
182#define GETNEXTARG() (\
183 check_next_arg(mrb, posarg, nextarg),\
184 (posarg = nextarg++, GETNTHARG(posarg)))
185
186#define GETARG() (!mrb_undef_p(nextvalue) ? nextvalue : GETNEXTARG())
187
188#define GETPOSARG(n) (\
189 check_pos_arg(mrb, posarg, n),\
190 (posarg = -1, GETNTHARG(n)))
191
192#define GETNTHARG(nth) \
193 ((nth >= argc) ? (mrb_raise(mrb, E_ARGUMENT_ERROR, "too few arguments"), mrb_undef_value()) : argv[nth])
194
195#define GETNAMEARG(id, name, len) (\
196 check_name_arg(mrb, posarg, name, len),\
197 (posarg = -2, mrb_hash_fetch(mrb, get_hash(mrb, &hash, argc, argv), id, mrb_undef_value())))
198
199#define GETNUM(n, val) \
200 for (; p < end && ISDIGIT(*p); p++) {\
201 int next_n = 10 * n + (*p - '0'); \
202 if (next_n / 10 != n) {\
203 mrb_raise(mrb, E_ARGUMENT_ERROR, #val " too big"); \
204 } \
205 n = next_n; \
206 } \
207 if (p >= end) { \
208 mrb_raise(mrb, E_ARGUMENT_ERROR, "malformed format string - %*[0-9]"); \
209 }
210
211#define GETASTER(num) do { \
212 mrb_value tmp_v; \
213 t = p++; \
214 n = 0; \
215 GETNUM(n, val); \
216 if (*p == '$') { \
217 tmp_v = GETPOSARG(n); \
218 } \
219 else { \
220 tmp_v = GETNEXTARG(); \
221 p = t; \
222 } \
223 num = mrb_int(mrb, tmp_v); \
224} while (0)
225
226static mrb_value
227get_hash(mrb_state *mrb, mrb_value *hash, int argc, const mrb_value *argv)
228{
229 mrb_value tmp;
230
231 if (!mrb_undef_p(*hash)) return *hash;
232 if (argc != 2) {
233 mrb_raise(mrb, E_ARGUMENT_ERROR, "one hash required");
234 }
235 tmp = mrb_check_convert_type(mrb, argv[1], MRB_TT_HASH, "Hash", "to_hash");
236 if (mrb_nil_p(tmp)) {
237 mrb_raise(mrb, E_ARGUMENT_ERROR, "one hash required");
238 }
239 return (*hash = tmp);
240}
241
242/*
243 * call-seq:
244 * format(format_string [, arguments...] ) -> string
245 * sprintf(format_string [, arguments...] ) -> string
246 *
247 * Returns the string resulting from applying <i>format_string</i> to
248 * any additional arguments. Within the format string, any characters
249 * other than format sequences are copied to the result.
250 *
251 * The syntax of a format sequence is follows.
252 *
253 * %[flags][width][.precision]type
254 *
255 * A format
256 * sequence consists of a percent sign, followed by optional flags,
257 * width, and precision indicators, then terminated with a field type
258 * character. The field type controls how the corresponding
259 * <code>sprintf</code> argument is to be interpreted, while the flags
260 * modify that interpretation.
261 *
262 * The field type characters are:
263 *
264 * Field | Integer Format
265 * ------+--------------------------------------------------------------
266 * b | Convert argument as a binary number.
267 * | Negative numbers will be displayed as a two's complement
268 * | prefixed with '..1'.
269 * B | Equivalent to 'b', but uses an uppercase 0B for prefix
270 * | in the alternative format by #.
271 * d | Convert argument as a decimal number.
272 * i | Identical to 'd'.
273 * o | Convert argument as an octal number.
274 * | Negative numbers will be displayed as a two's complement
275 * | prefixed with '..7'.
276 * u | Identical to 'd'.
277 * x | Convert argument as a hexadecimal number.
278 * | Negative numbers will be displayed as a two's complement
279 * | prefixed with '..f' (representing an infinite string of
280 * | leading 'ff's).
281 * X | Equivalent to 'x', but uses uppercase letters.
282 *
283 * Field | Float Format
284 * ------+--------------------------------------------------------------
285 * e | Convert floating point argument into exponential notation
286 * | with one digit before the decimal point as [-]d.dddddde[+-]dd.
287 * | The precision specifies the number of digits after the decimal
288 * | point (defaulting to six).
289 * E | Equivalent to 'e', but uses an uppercase E to indicate
290 * | the exponent.
291 * f | Convert floating point argument as [-]ddd.dddddd,
292 * | where the precision specifies the number of digits after
293 * | the decimal point.
294 * g | Convert a floating point number using exponential form
295 * | if the exponent is less than -4 or greater than or
296 * | equal to the precision, or in dd.dddd form otherwise.
297 * | The precision specifies the number of significant digits.
298 * G | Equivalent to 'g', but use an uppercase 'E' in exponent form.
299 * a | Convert floating point argument as [-]0xh.hhhhp[+-]dd,
300 * | which is consisted from optional sign, "0x", fraction part
301 * | as hexadecimal, "p", and exponential part as decimal.
302 * A | Equivalent to 'a', but use uppercase 'X' and 'P'.
303 *
304 * Field | Other Format
305 * ------+--------------------------------------------------------------
306 * c | Argument is the numeric code for a single character or
307 * | a single character string itself.
308 * p | The valuing of argument.inspect.
309 * s | Argument is a string to be substituted. If the format
310 * | sequence contains a precision, at most that many characters
311 * | will be copied.
312 * % | A percent sign itself will be displayed. No argument taken.
313 *
314 * The flags modifies the behavior of the formats.
315 * The flag characters are:
316 *
317 * Flag | Applies to | Meaning
318 * ---------+---------------+-----------------------------------------
319 * space | bBdiouxX | Leave a space at the start of
320 * | aAeEfgG | non-negative numbers.
321 * | (numeric fmt) | For 'o', 'x', 'X', 'b' and 'B', use
322 * | | a minus sign with absolute value for
323 * | | negative values.
324 * ---------+---------------+-----------------------------------------
325 * (digit)$ | all | Specifies the absolute argument number
326 * | | for this field. Absolute and relative
327 * | | argument numbers cannot be mixed in a
328 * | | sprintf string.
329 * ---------+---------------+-----------------------------------------
330 * # | bBoxX | Use an alternative format.
331 * | aAeEfgG | For the conversions 'o', increase the precision
332 * | | until the first digit will be '0' if
333 * | | it is not formatted as complements.
334 * | | For the conversions 'x', 'X', 'b' and 'B'
335 * | | on non-zero, prefix the result with "0x",
336 * | | "0X", "0b" and "0B", respectively.
337 * | | For 'a', 'A', 'e', 'E', 'f', 'g', and 'G',
338 * | | force a decimal point to be added,
339 * | | even if no digits follow.
340 * | | For 'g' and 'G', do not remove trailing zeros.
341 * ---------+---------------+-----------------------------------------
342 * + | bBdiouxX | Add a leading plus sign to non-negative
343 * | aAeEfgG | numbers.
344 * | (numeric fmt) | For 'o', 'x', 'X', 'b' and 'B', use
345 * | | a minus sign with absolute value for
346 * | | negative values.
347 * ---------+---------------+-----------------------------------------
348 * - | all | Left-justify the result of this conversion.
349 * ---------+---------------+-----------------------------------------
350 * 0 (zero) | bBdiouxX | Pad with zeros, not spaces.
351 * | aAeEfgG | For 'o', 'x', 'X', 'b' and 'B', radix-1
352 * | (numeric fmt) | is used for negative numbers formatted as
353 * | | complements.
354 * ---------+---------------+-----------------------------------------
355 * * | all | Use the next argument as the field width.
356 * | | If negative, left-justify the result. If the
357 * | | asterisk is followed by a number and a dollar
358 * | | sign, use the indicated argument as the width.
359 *
360 * Examples of flags:
361 *
362 * # '+' and space flag specifies the sign of non-negative numbers.
363 * sprintf("%d", 123) #=> "123"
364 * sprintf("%+d", 123) #=> "+123"
365 * sprintf("% d", 123) #=> " 123"
366 *
367 * # '#' flag for 'o' increases number of digits to show '0'.
368 * # '+' and space flag changes format of negative numbers.
369 * sprintf("%o", 123) #=> "173"
370 * sprintf("%#o", 123) #=> "0173"
371 * sprintf("%+o", -123) #=> "-173"
372 * sprintf("%o", -123) #=> "..7605"
373 * sprintf("%#o", -123) #=> "..7605"
374 *
375 * # '#' flag for 'x' add a prefix '0x' for non-zero numbers.
376 * # '+' and space flag disables complements for negative numbers.
377 * sprintf("%x", 123) #=> "7b"
378 * sprintf("%#x", 123) #=> "0x7b"
379 * sprintf("%+x", -123) #=> "-7b"
380 * sprintf("%x", -123) #=> "..f85"
381 * sprintf("%#x", -123) #=> "0x..f85"
382 * sprintf("%#x", 0) #=> "0"
383 *
384 * # '#' for 'X' uses the prefix '0X'.
385 * sprintf("%X", 123) #=> "7B"
386 * sprintf("%#X", 123) #=> "0X7B"
387 *
388 * # '#' flag for 'b' add a prefix '0b' for non-zero numbers.
389 * # '+' and space flag disables complements for negative numbers.
390 * sprintf("%b", 123) #=> "1111011"
391 * sprintf("%#b", 123) #=> "0b1111011"
392 * sprintf("%+b", -123) #=> "-1111011"
393 * sprintf("%b", -123) #=> "..10000101"
394 * sprintf("%#b", -123) #=> "0b..10000101"
395 * sprintf("%#b", 0) #=> "0"
396 *
397 * # '#' for 'B' uses the prefix '0B'.
398 * sprintf("%B", 123) #=> "1111011"
399 * sprintf("%#B", 123) #=> "0B1111011"
400 *
401 * # '#' for 'e' forces to show the decimal point.
402 * sprintf("%.0e", 1) #=> "1e+00"
403 * sprintf("%#.0e", 1) #=> "1.e+00"
404 *
405 * # '#' for 'f' forces to show the decimal point.
406 * sprintf("%.0f", 1234) #=> "1234"
407 * sprintf("%#.0f", 1234) #=> "1234."
408 *
409 * # '#' for 'g' forces to show the decimal point.
410 * # It also disables stripping lowest zeros.
411 * sprintf("%g", 123.4) #=> "123.4"
412 * sprintf("%#g", 123.4) #=> "123.400"
413 * sprintf("%g", 123456) #=> "123456"
414 * sprintf("%#g", 123456) #=> "123456."
415 *
416 * The field width is an optional integer, followed optionally by a
417 * period and a precision. The width specifies the minimum number of
418 * characters that will be written to the result for this field.
419 *
420 * Examples of width:
421 *
422 * # padding is done by spaces, width=20
423 * # 0 or radix-1. <------------------>
424 * sprintf("%20d", 123) #=> " 123"
425 * sprintf("%+20d", 123) #=> " +123"
426 * sprintf("%020d", 123) #=> "00000000000000000123"
427 * sprintf("%+020d", 123) #=> "+0000000000000000123"
428 * sprintf("% 020d", 123) #=> " 0000000000000000123"
429 * sprintf("%-20d", 123) #=> "123 "
430 * sprintf("%-+20d", 123) #=> "+123 "
431 * sprintf("%- 20d", 123) #=> " 123 "
432 * sprintf("%020x", -123) #=> "..ffffffffffffffff85"
433 *
434 * For
435 * numeric fields, the precision controls the number of decimal places
436 * displayed. For string fields, the precision determines the maximum
437 * number of characters to be copied from the string. (Thus, the format
438 * sequence <code>%10.10s</code> will always contribute exactly ten
439 * characters to the result.)
440 *
441 * Examples of precisions:
442 *
443 * # precision for 'd', 'o', 'x' and 'b' is
444 * # minimum number of digits <------>
445 * sprintf("%20.8d", 123) #=> " 00000123"
446 * sprintf("%20.8o", 123) #=> " 00000173"
447 * sprintf("%20.8x", 123) #=> " 0000007b"
448 * sprintf("%20.8b", 123) #=> " 01111011"
449 * sprintf("%20.8d", -123) #=> " -00000123"
450 * sprintf("%20.8o", -123) #=> " ..777605"
451 * sprintf("%20.8x", -123) #=> " ..ffff85"
452 * sprintf("%20.8b", -11) #=> " ..110101"
453 *
454 * # "0x" and "0b" for '#x' and '#b' is not counted for
455 * # precision but "0" for '#o' is counted. <------>
456 * sprintf("%#20.8d", 123) #=> " 00000123"
457 * sprintf("%#20.8o", 123) #=> " 00000173"
458 * sprintf("%#20.8x", 123) #=> " 0x0000007b"
459 * sprintf("%#20.8b", 123) #=> " 0b01111011"
460 * sprintf("%#20.8d", -123) #=> " -00000123"
461 * sprintf("%#20.8o", -123) #=> " ..777605"
462 * sprintf("%#20.8x", -123) #=> " 0x..ffff85"
463 * sprintf("%#20.8b", -11) #=> " 0b..110101"
464 *
465 * # precision for 'e' is number of
466 * # digits after the decimal point <------>
467 * sprintf("%20.8e", 1234.56789) #=> " 1.23456789e+03"
468 *
469 * # precision for 'f' is number of
470 * # digits after the decimal point <------>
471 * sprintf("%20.8f", 1234.56789) #=> " 1234.56789000"
472 *
473 * # precision for 'g' is number of
474 * # significant digits <------->
475 * sprintf("%20.8g", 1234.56789) #=> " 1234.5679"
476 *
477 * # <------->
478 * sprintf("%20.8g", 123456789) #=> " 1.2345679e+08"
479 *
480 * # precision for 's' is
481 * # maximum number of characters <------>
482 * sprintf("%20.8s", "string test") #=> " string t"
483 *
484 * Examples:
485 *
486 * sprintf("%d %04x", 123, 123) #=> "123 007b"
487 * sprintf("%08b '%4s'", 123, 123) #=> "01111011 ' 123'"
488 * sprintf("%1$*2$s %2$d %1$s", "hello", 8) #=> " hello 8 hello"
489 * sprintf("%1$*2$s %2$d", "hello", -8) #=> "hello -8"
490 * sprintf("%+g:% g:%-g", 1.23, 1.23, 1.23) #=> "+1.23: 1.23:1.23"
491 * sprintf("%u", -123) #=> "-123"
492 *
493 * For more complex formatting, Ruby supports a reference by name.
494 * %<name>s style uses format style, but %{name} style doesn't.
495 *
496 * Exapmles:
497 * sprintf("%<foo>d : %<bar>f", { :foo => 1, :bar => 2 })
498 * #=> 1 : 2.000000
499 * sprintf("%{foo}f", { :foo => 1 })
500 * # => "1f"
501 */
502
503mrb_value
504mrb_f_sprintf(mrb_state *mrb, mrb_value obj)
505{
506 mrb_int argc;
507 mrb_value *argv;
508
509 mrb_get_args(mrb, "*", &argv, &argc);
510
511 if (argc <= 0) {
512 mrb_raise(mrb, E_ARGUMENT_ERROR, "too few arguments");
513 return mrb_nil_value();
514 }
515 else {
516 return mrb_str_format(mrb, argc - 1, argv + 1, argv[0]);
517 }
518}
519
520mrb_value
521mrb_str_format(mrb_state *mrb, int argc, const mrb_value *argv, mrb_value fmt)
522{
523 const char *p, *end;
524 char *buf;
525 mrb_int blen;
526 mrb_int bsiz;
527 mrb_value result;
528 mrb_int n;
529 mrb_int width;
530 mrb_int prec;
531 int flags = FNONE;
532 int nextarg = 1;
533 int posarg = 0;
534 mrb_value nextvalue;
535 mrb_value str;
536 mrb_value hash = mrb_undef_value();
537
538#define CHECK_FOR_WIDTH(f) \
539 if ((f) & FWIDTH) { \
540 mrb_raise(mrb, E_ARGUMENT_ERROR, "width given twice"); \
541 } \
542 if ((f) & FPREC0) { \
543 mrb_raise(mrb, E_ARGUMENT_ERROR, "width after precision"); \
544 }
545#define CHECK_FOR_FLAGS(f) \
546 if ((f) & FWIDTH) { \
547 mrb_raise(mrb, E_ARGUMENT_ERROR, "flag after width"); \
548 } \
549 if ((f) & FPREC0) { \
550 mrb_raise(mrb, E_ARGUMENT_ERROR, "flag after precision"); \
551 }
552
553 ++argc;
554 --argv;
555 fmt = mrb_str_to_str(mrb, fmt);
556 p = RSTRING_PTR(fmt);
557 end = p + RSTRING_LEN(fmt);
558 blen = 0;
559 bsiz = 120;
560 result = mrb_str_buf_new(mrb, bsiz);
561 buf = RSTRING_PTR(result);
562 memset(buf, 0, bsiz);
563
564 for (; p < end; p++) {
565 const char *t;
566 mrb_sym id = 0;
567
568 for (t = p; t < end && *t != '%'; t++) ;
569 if (t + 1 == end) ++t;
570 PUSH(p, t - p);
571 if (t >= end)
572 goto sprint_exit; /* end of fmt string */
573
574 p = t + 1; /* skip '%' */
575
576 width = prec = -1;
577 nextvalue = mrb_undef_value();
578
579retry:
580 switch (*p) {
581 default:
582 mrb_raisef(mrb, E_ARGUMENT_ERROR, "malformed format string - \\%%S", mrb_str_new(mrb, p, 1));
583 break;
584
585 case ' ':
586 CHECK_FOR_FLAGS(flags);
587 flags |= FSPACE;
588 p++;
589 goto retry;
590
591 case '#':
592 CHECK_FOR_FLAGS(flags);
593 flags |= FSHARP;
594 p++;
595 goto retry;
596
597 case '+':
598 CHECK_FOR_FLAGS(flags);
599 flags |= FPLUS;
600 p++;
601 goto retry;
602
603 case '-':
604 CHECK_FOR_FLAGS(flags);
605 flags |= FMINUS;
606 p++;
607 goto retry;
608
609 case '0':
610 CHECK_FOR_FLAGS(flags);
611 flags |= FZERO;
612 p++;
613 goto retry;
614
615 case '1': case '2': case '3': case '4':
616 case '5': case '6': case '7': case '8': case '9':
617 n = 0;
618 GETNUM(n, width);
619 if (*p == '$') {
620 if (!mrb_undef_p(nextvalue)) {
621 mrb_raisef(mrb, E_ARGUMENT_ERROR, "value given twice - %S$", mrb_fixnum_value(n));
622 }
623 nextvalue = GETPOSARG(n);
624 p++;
625 goto retry;
626 }
627 CHECK_FOR_WIDTH(flags);
628 width = n;
629 flags |= FWIDTH;
630 goto retry;
631
632 case '<':
633 case '{': {
634 const char *start = p;
635 char term = (*p == '<') ? '>' : '}';
636 mrb_value symname;
637
638 for (; p < end && *p != term; )
639 p++;
640 if (id) {
641 mrb_raisef(mrb, E_ARGUMENT_ERROR, "name%S after <%S>",
642 mrb_str_new(mrb, start, p - start + 1), mrb_sym2str(mrb, id));
643 }
644 symname = mrb_str_new(mrb, start + 1, p - start - 1);
645 id = mrb_intern_str(mrb, symname);
646 nextvalue = GETNAMEARG(mrb_symbol_value(id), start, (int)(p - start + 1));
647 if (mrb_undef_p(nextvalue)) {
648 mrb_raisef(mrb, E_KEY_ERROR, "key%S not found", mrb_str_new(mrb, start, p - start + 1));
649 }
650 if (term == '}') goto format_s;
651 p++;
652 goto retry;
653 }
654
655 case '*':
656 CHECK_FOR_WIDTH(flags);
657 flags |= FWIDTH;
658 GETASTER(width);
659 if (width < 0) {
660 flags |= FMINUS;
661 width = -width;
662 }
663 p++;
664 goto retry;
665
666 case '.':
667 if (flags & FPREC0) {
668 mrb_raise(mrb, E_ARGUMENT_ERROR, "precision given twice");
669 }
670 flags |= FPREC|FPREC0;
671
672 prec = 0;
673 p++;
674 if (*p == '*') {
675 GETASTER(prec);
676 if (prec < 0) { /* ignore negative precision */
677 flags &= ~FPREC;
678 }
679 p++;
680 goto retry;
681 }
682
683 GETNUM(prec, precision);
684 goto retry;
685
686 case '\n':
687 case '\0':
688 p--;
689 /* fallthrough */
690 case '%':
691 if (flags != FNONE) {
692 mrb_raise(mrb, E_ARGUMENT_ERROR, "invalid format character - %");
693 }
694 PUSH("%", 1);
695 break;
696
697 case 'c': {
698 mrb_value val = GETARG();
699 mrb_value tmp;
700 char *c;
701
702 tmp = mrb_check_string_type(mrb, val);
703 if (!mrb_nil_p(tmp)) {
704 if (mrb_fixnum(mrb_funcall(mrb, tmp, "size", 0)) != 1 ) {
705 mrb_raise(mrb, E_ARGUMENT_ERROR, "%c requires a character");
706 }
707 }
708 else if (mrb_fixnum_p(val)) {
709 tmp = mrb_funcall(mrb, val, "chr", 0);
710 }
711 else {
712 mrb_raise(mrb, E_ARGUMENT_ERROR, "invalid character");
713 }
714 mrb_check_type(mrb, tmp, MRB_TT_STRING);
715 c = RSTRING_PTR(tmp);
716 n = RSTRING_LEN(tmp);
717 if (!(flags & FWIDTH)) {
718 PUSH(c, n);
719 }
720 else if ((flags & FMINUS)) {
721 PUSH(c, n);
722 if (width>0) FILL(' ', width-1);
723 }
724 else {
725 if (width>0) FILL(' ', width-1);
726 PUSH(c, n);
727 }
728 }
729 break;
730
731 case 's':
732 case 'p':
733 format_s:
734 {
735 mrb_value arg = GETARG();
736 mrb_int len;
737 mrb_int slen;
738
739 if (*p == 'p') arg = mrb_inspect(mrb, arg);
740 str = mrb_obj_as_string(mrb, arg);
741 len = RSTRING_LEN(str);
742 if (RSTRING(result)->flags & MRB_STR_EMBED) {
743 mrb_int tmp_n = len;
744 RSTRING(result)->flags &= ~MRB_STR_EMBED_LEN_MASK;
745 RSTRING(result)->flags |= tmp_n << MRB_STR_EMBED_LEN_SHIFT;
746 }
747 else {
748 RSTRING(result)->as.heap.len = blen;
749 }
750 if (flags&(FPREC|FWIDTH)) {
751 slen = RSTRING_LEN(str);
752 if (slen < 0) {
753 mrb_raise(mrb, E_ARGUMENT_ERROR, "invalid mbstring sequence");
754 }
755 if ((flags&FPREC) && (prec < slen)) {
756 char *p = RSTRING_PTR(str) + prec;
757 slen = prec;
758 len = p - RSTRING_PTR(str);
759 }
760 /* need to adjust multi-byte string pos */
761 if ((flags&FWIDTH) && (width > slen)) {
762 width -= (int)slen;
763 if (!(flags&FMINUS)) {
764 FILL(' ', width);
765 }
766 PUSH(RSTRING_PTR(str), len);
767 if (flags&FMINUS) {
768 FILL(' ', width);
769 }
770 break;
771 }
772 }
773 PUSH(RSTRING_PTR(str), len);
774 }
775 break;
776
777 case 'd':
778 case 'i':
779 case 'o':
780 case 'x':
781 case 'X':
782 case 'b':
783 case 'B':
784 case 'u': {
785 mrb_value val = GETARG();
786 char nbuf[68], *s;
787 const char *prefix = NULL;
788 int sign = 0, dots = 0;
789 char sc = 0;
790 mrb_int v = 0;
791 int base;
792 mrb_int len;
793
794 switch (*p) {
795 case 'd':
796 case 'i':
797 sign = 1; break;
798 default:
799 break;
800 }
801 if (flags & FSHARP) {
802 switch (*p) {
803 case 'o': prefix = "0"; break;
804 case 'x': prefix = "0x"; break;
805 case 'X': prefix = "0X"; break;
806 case 'b': prefix = "0b"; break;
807 case 'B': prefix = "0B"; break;
808 default: break;
809 }
810 }
811
812 bin_retry:
813 switch (mrb_type(val)) {
814 case MRB_TT_FLOAT:
815 val = mrb_flo_to_fixnum(mrb, val);
816 if (mrb_fixnum_p(val)) goto bin_retry;
817 break;
818 case MRB_TT_STRING:
819 val = mrb_str_to_inum(mrb, val, 0, TRUE);
820 goto bin_retry;
821 case MRB_TT_FIXNUM:
822 v = mrb_fixnum(val);
823 break;
824 default:
825 val = mrb_Integer(mrb, val);
826 goto bin_retry;
827 }
828
829 switch (*p) {
830 case 'o':
831 base = 8; break;
832 case 'x':
833 case 'X':
834 base = 16; break;
835 case 'b':
836 case 'B':
837 base = 2; break;
838 case 'u':
839 case 'd':
840 case 'i':
841 default:
842 base = 10; break;
843 }
844
845 if (base == 2) {
846 if (v < 0 && !sign) {
847 val = mrb_fix2binstr(mrb, mrb_fixnum_value(v), base);
848 dots = 1;
849 }
850 else {
851 val = mrb_fixnum_to_str(mrb, mrb_fixnum_value(v), base);
852 }
853 }
854 if (sign) {
855 if (v > 0) {
856 if (flags & FPLUS) {
857 sc = '+';
858 width--;
859 }
860 else if (flags & FSPACE) {
861 sc = ' ';
862 width--;
863 }
864 }
865 switch (base) {
866 case 2:
867 strncpy(nbuf, RSTRING_PTR(val), sizeof(nbuf));
868 break;
869 case 8:
870 snprintf(nbuf, sizeof(nbuf), "%" MRB_PRIo, v);
871 break;
872 case 10:
873 snprintf(nbuf, sizeof(nbuf), "%" MRB_PRId, v);
874 break;
875 case 16:
876 snprintf(nbuf, sizeof(nbuf), "%" MRB_PRIx, v);
877 break;
878 }
879 s = nbuf;
880 }
881 else {
882 s = nbuf;
883 if (base != 10 && v < 0) {
884 dots = 1;
885 }
886 switch (base) {
887 case 2:
888 strncpy(++s, RSTRING_PTR(val), sizeof(nbuf)-1);
889 break;
890 case 8:
891 snprintf(++s, sizeof(nbuf)-1, "%" MRB_PRIo, v);
892 break;
893 case 10:
894 snprintf(++s, sizeof(nbuf)-1, "%" MRB_PRId, v);
895 break;
896 case 16:
897 snprintf(++s, sizeof(nbuf)-1, "%" MRB_PRIx, v);
898 break;
899 }
900 if (v < 0) {
901 char d;
902
903 s = remove_sign_bits(s, base);
904 switch (base) {
905 case 16: d = 'f'; break;
906 case 8: d = '7'; break;
907 case 2: d = '1'; break;
908 default: d = 0; break;
909 }
910
911 if (d && *s != d) {
912 *--s = d;
913 }
914 }
915 }
916 {
917 size_t size;
918 size = strlen(s);
919 /* PARANOID: assert(size <= MRB_INT_MAX) */
920 len = (mrb_int)size;
921 }
922
923 if (dots) {
924 prec -= 2;
925 width -= 2;
926 }
927
928 if (*p == 'X') {
929 char *pp = s;
930 int c;
931 while ((c = (int)(unsigned char)*pp) != 0) {
932 *pp = toupper(c);
933 pp++;
934 }
935 }
936
937 if (prefix && !prefix[1]) { /* octal */
938 if (dots) {
939 prefix = NULL;
940 }
941 else if (len == 1 && *s == '0') {
942 len = 0;
943 if (flags & FPREC) prec--;
944 }
945 else if ((flags & FPREC) && (prec > len)) {
946 prefix = NULL;
947 }
948 }
949 else if (len == 1 && *s == '0') {
950 prefix = NULL;
951 }
952
953 if (prefix) {
954 size_t size;
955 size = strlen(prefix);
956 /* PARANOID: assert(size <= MRB_INT_MAX).
957 * this check is absolutely paranoid. */
958 width -= (mrb_int)size;
959 }
960
961 if ((flags & (FZERO|FMINUS|FPREC)) == FZERO) {
962 prec = width;
963 width = 0;
964 }
965 else {
966 if (prec < len) {
967 if (!prefix && prec == 0 && len == 1 && *s == '0') len = 0;
968 prec = len;
969 }
970 width -= prec;
971 }
972
973 if (!(flags&FMINUS) && width > 0) {
974 FILL(' ', width);
975 }
976
977 if (sc) PUSH(&sc, 1);
978
979 if (prefix) {
980 int plen = (int)strlen(prefix);
981 PUSH(prefix, plen);
982 }
983 if (dots) PUSH("..", 2);
984
985 if (prec > len) {
986 CHECK(prec - len);
987 if (v < 0) {
988 char c = sign_bits(base, p);
989 FILL(c, prec - len);
990 }
991 else if ((flags & (FMINUS|FPREC)) != FMINUS) {
992 char c = '0';
993 FILL(c, prec - len);
994 }
995 }
996 PUSH(s, len);
997 if (width > 0) {
998 FILL(' ', width);
999 }
1000 }
1001 break;
1002
1003 case 'f':
1004 case 'g':
1005 case 'G':
1006 case 'e':
1007 case 'E':
1008 case 'a':
1009 case 'A': {
1010 mrb_value val = GETARG();
1011 double fval;
1012 int i, need = 6;
1013 char fbuf[32];
1014
1015 fval = mrb_float(mrb_Float(mrb, val));
1016 if (!isfinite(fval)) {
1017 const char *expr;
1018 const int elen = 3;
1019 char sign = '\0';
1020
1021 if (isnan(fval)) {
1022 expr = "NaN";
1023 }
1024 else {
1025 expr = "Inf";
1026 }
1027 need = elen;
1028 if (!isnan(fval) && fval < 0.0)
1029 sign = '-';
1030 else if (flags & (FPLUS|FSPACE))
1031 sign = (flags & FPLUS) ? '+' : ' ';
1032 if (sign)
1033 ++need;
1034 if ((flags & FWIDTH) && need < width)
1035 need = width;
1036
1037 if (need < 0) {
1038 mrb_raise(mrb, E_ARGUMENT_ERROR, "width too big");
1039 }
1040 FILL(' ', need);
1041 if (flags & FMINUS) {
1042 if (sign)
1043 buf[blen - need--] = sign;
1044 memcpy(&buf[blen - need], expr, elen);
1045 }
1046 else {
1047 if (sign)
1048 buf[blen - elen - 1] = sign;
1049 memcpy(&buf[blen - elen], expr, elen);
1050 }
1051 break;
1052 }
1053
1054 fmt_setup(fbuf, sizeof(fbuf), *p, flags, width, prec);
1055 need = 0;
1056 if (*p != 'e' && *p != 'E') {
1057 i = INT_MIN;
1058 frexp(fval, &i);
1059 if (i > 0)
1060 need = BIT_DIGITS(i);
1061 }
1062 need += (flags&FPREC) ? prec : 6;
1063 if ((flags&FWIDTH) && need < width)
1064 need = width;
1065 need += 20;
1066 if (need <= 0) {
1067 mrb_raise(mrb, E_ARGUMENT_ERROR,
1068 (width > prec ? "width too big" : "prec too big"));
1069 }
1070
1071 CHECK(need);
1072 n = snprintf(&buf[blen], need, fbuf, fval);
1073 if (n < 0) {
1074 mrb_raise(mrb, E_RUNTIME_ERROR, "formatting error");
1075 }
1076 blen += n;
1077 }
1078 break;
1079 }
1080 flags = FNONE;
1081 }
1082
1083 sprint_exit:
1084#if 0
1085 /* XXX - We cannot validate the number of arguments if (digit)$ style used.
1086 */
1087 if (posarg >= 0 && nextarg < argc) {
1088 const char *mesg = "too many arguments for format string";
1089 if (mrb_test(ruby_debug)) mrb_raise(mrb, E_ARGUMENT_ERROR, mesg);
1090 if (mrb_test(ruby_verbose)) mrb_warn(mrb, "%S", mrb_str_new_cstr(mrb, mesg));
1091 }
1092#endif
1093 mrb_str_resize(mrb, result, blen);
1094
1095 return result;
1096}
1097
1098static void
1099fmt_setup(char *buf, size_t size, int c, int flags, mrb_int width, mrb_int prec)
1100{
1101 char *end = buf + size;
1102 int n;
1103
1104 *buf++ = '%';
1105 if (flags & FSHARP) *buf++ = '#';
1106 if (flags & FPLUS) *buf++ = '+';
1107 if (flags & FMINUS) *buf++ = '-';
1108 if (flags & FZERO) *buf++ = '0';
1109 if (flags & FSPACE) *buf++ = ' ';
1110
1111 if (flags & FWIDTH) {
1112 n = snprintf(buf, end - buf, "%d", (int)width);
1113 buf += n;
1114 }
1115
1116 if (flags & FPREC) {
1117 n = snprintf(buf, end - buf, ".%d", (int)prec);
1118 buf += n;
1119 }
1120
1121 *buf++ = c;
1122 *buf = '\0';
1123}
Note: See TracBrowser for help on using the repository browser.