2 Copyright (C) 2001-2009, Parrot Foundation.
7 src/spf_render.c - Parrot sprintf
11 Implements the main function that drives the C<Parrot_sprintf> family
12 and its utility functions.
14 =head2 Utility Functions
24 #include "parrot/parrot.h"
25 #include "spf_render.str"
36 typedef struct SpfInfo_tag
{
54 /* HEADERIZER HFILE: include/parrot/misc.h */
56 /* HEADERIZER BEGIN: static */
57 /* Don't modify between HEADERIZER BEGIN / HEADERIZER END. Your changes will be lost. */
59 static void gen_sprintf_call(
61 ARGMOD(SpfInfo
*info
),
63 __attribute__nonnull__(1)
64 __attribute__nonnull__(2)
68 PARROT_WARN_UNUSED_RESULT
69 PARROT_CANNOT_RETURN_NULL
70 static STRING
* handle_flags(PARROT_INTERP
,
71 ARGIN(const SpfInfo
*info
),
74 ARGIN_NULLOK(STRING
* prefix
))
75 __attribute__nonnull__(1)
76 __attribute__nonnull__(2)
77 __attribute__nonnull__(3);
79 PARROT_CANNOT_RETURN_NULL
80 static STRING
* str_concat_w_flags(PARROT_INTERP
,
82 ARGIN(const SpfInfo
*info
),
84 ARGIN_NULLOK(STRING
*prefix
))
85 __attribute__nonnull__(1)
86 __attribute__nonnull__(2)
87 __attribute__nonnull__(3)
88 __attribute__nonnull__(4)
92 #define ASSERT_ARGS_gen_sprintf_call __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
93 PARROT_ASSERT_ARG(out) \
94 , PARROT_ASSERT_ARG(info))
95 #define ASSERT_ARGS_handle_flags __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
96 PARROT_ASSERT_ARG(interp) \
97 , PARROT_ASSERT_ARG(info) \
98 , PARROT_ASSERT_ARG(str))
99 #define ASSERT_ARGS_str_concat_w_flags __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
100 PARROT_ASSERT_ARG(interp) \
101 , PARROT_ASSERT_ARG(dest) \
102 , PARROT_ASSERT_ARG(info) \
103 , PARROT_ASSERT_ARG(src))
104 /* Don't modify between HEADERIZER BEGIN / HEADERIZER END. Your changes will be lost. */
105 /* HEADERIZER END: static */
108 /* Per Dan's orders, we will not use sprintf if snprintf isn't
112 # define snprintf _snprintf
117 =item C<static STRING * handle_flags(PARROT_INTERP, const SpfInfo *info, STRING
118 *str, INTVAL is_int_type, STRING* prefix)>
120 Handles C<+>, C<->, C<0>, C<#>, space, width, and prec.
126 PARROT_WARN_UNUSED_RESULT
127 PARROT_CANNOT_RETURN_NULL
129 handle_flags(PARROT_INTERP
, ARGIN(const SpfInfo
*info
), ARGIN(STRING
*str
),
130 INTVAL is_int_type
, ARGIN_NULLOK(STRING
* prefix
))
132 ASSERT_ARGS(handle_flags
)
133 UINTVAL len
= Parrot_str_byte_length(interp
, str
);
136 if (info
->flags
& FLAG_PREC
&& info
->prec
== 0 &&
138 string_ord(interp
, str
, 0) == '0') {
139 str
= Parrot_str_chopn(interp
, str
, len
);
143 if (!len
|| string_ord(interp
, str
, 0) != '-') {
144 if (info
->flags
& FLAG_PLUS
) {
145 STRING
* const cs
= CONST_STRING(interp
, "+");
146 str
= Parrot_str_concat(interp
, cs
, str
);
149 else if (info
->flags
& FLAG_SPACE
) {
150 STRING
* const cs
= CONST_STRING(interp
, " ");
151 str
= Parrot_str_concat(interp
, cs
, str
);
157 if ((info
->flags
& FLAG_SHARP
) && prefix
) {
158 str
= Parrot_str_concat(interp
, prefix
, str
);
159 len
+= Parrot_str_byte_length(interp
, prefix
);
161 /* XXX sharp + fill ??? */
164 /* string precision */
165 if (info
->flags
& FLAG_PREC
&& info
->prec
== 0) {
166 str
= Parrot_str_chopn(interp
, str
, len
);
169 else if (info
->flags
& FLAG_PREC
&& info
->prec
< len
) {
170 str
= Parrot_str_chopn(interp
, str
, -(INTVAL
)(info
->prec
));
175 if ((info
->flags
& FLAG_WIDTH
) && info
->width
> len
) {
176 STRING
* const filler
=
177 ((info
->flags
& FLAG_ZERO
) && !(info
->flags
& FLAG_MINUS
))
178 ? CONST_STRING(interp
, "0")
179 : CONST_STRING(interp
, " ");
180 STRING
* const fill
= Parrot_str_repeat(interp
, filler
, info
->width
- len
);
182 if (info
->flags
& FLAG_MINUS
) { /* left-align */
183 str
= Parrot_str_concat(interp
, str
, fill
);
185 else { /* right-align */
186 /* signed and zero padded */
187 if (info
->flags
& FLAG_ZERO
188 && (string_ord(interp
, str
, 0) == '-' ||
189 string_ord(interp
, str
, 0) == '+')) {
192 temp
= Parrot_str_substr(interp
, str
, 1, len
-1);
193 str
= Parrot_str_chopn(interp
, str
, -1);
194 str
= Parrot_str_concat(interp
, str
, fill
);
195 str
= Parrot_str_concat(interp
, str
, temp
);
198 str
= Parrot_str_concat(interp
, fill
, str
);
207 =item C<static STRING* str_concat_w_flags(PARROT_INTERP, STRING *dest, const
208 SpfInfo *info, STRING *src, STRING *prefix)>
210 Used by Parrot_sprintf_format. Prepends supplied prefix for numeric
211 values. (e.g. 0x for hex.)
213 Returns the pointer to the modified string.
219 PARROT_CANNOT_RETURN_NULL
221 str_concat_w_flags(PARROT_INTERP
, ARGOUT(STRING
*dest
), ARGIN(const SpfInfo
*info
),
222 ARGMOD(STRING
*src
), ARGIN_NULLOK(STRING
*prefix
))
224 ASSERT_ARGS(str_concat_w_flags
)
225 src
= handle_flags(interp
, info
, src
, 1, prefix
);
226 dest
= Parrot_str_concat(interp
, dest
, src
);
232 =item C<static void gen_sprintf_call(char *out, SpfInfo *info, int thingy)>
234 Turn the info structure back into an sprintf format. Far from being
235 pointless, this is used to call C<snprintf()> when we're confronted with
243 gen_sprintf_call(ARGOUT(char *out
), ARGMOD(SpfInfo
*info
), int thingy
)
245 ASSERT_ARGS(gen_sprintf_call
)
247 const int flags
= info
->flags
;
252 if (flags
& FLAG_MINUS
)
255 if (flags
& FLAG_PLUS
)
258 if (flags
& FLAG_ZERO
)
261 if (flags
& FLAG_SPACE
)
264 if (flags
& FLAG_SHARP
)
267 if (flags
& FLAG_WIDTH
) {
268 if (info
->width
> PARROT_SPRINTF_BUFFER_SIZE
- 1)
269 info
->width
= PARROT_SPRINTF_BUFFER_SIZE
;
271 p
+= sprintf(p
, "%u", (unsigned)info
->width
);
274 if (flags
& FLAG_PREC
) {
275 if (info
->prec
> PARROT_SPRINTF_MAX_PREC
)
276 info
->prec
= PARROT_SPRINTF_MAX_PREC
;
279 p
+= sprintf(p
, "%u", (unsigned)info
->prec
);
283 if (thingy
== 'd' || thingy
== 'i' ||thingy
== 'u') {
284 /* the u?int isa HUGEU?INTVAL aka long long
285 * the 'll' modifier is specced in susv3 - hopefully all our
286 * compilers support it too */
298 =item C<STRING * Parrot_sprintf_format(PARROT_INTERP, const STRING *pat,
301 This is the engine that does all the formatting.
307 PARROT_WARN_UNUSED_RESULT
308 PARROT_CANNOT_RETURN_NULL
310 Parrot_sprintf_format(PARROT_INTERP
, ARGIN(const STRING
*pat
), ARGMOD(SPRINTF_OBJ
*obj
))
312 ASSERT_ARGS(Parrot_sprintf_format
)
316 const INTVAL pat_len
= (INTVAL
)Parrot_str_byte_length(interp
, pat
);
319 /* start with a buffer; double the pattern length to avoid realloc #1 */
320 STRING
*targ
= Parrot_str_new_noinit(interp
, enum_stringrep_one
, pat_len
* 2);
322 /* ts is used almost universally as an intermediate target;
323 * tc is used as a temporary buffer by Parrot_str_from_uint and
324 * as a target by gen_sprintf_call.
326 STRING
*substr
= NULL
;
327 char tc
[PARROT_SPRINTF_BUFFER_SIZE
];
329 for (i
= 0; i
< pat_len
; ++i
) {
330 if (string_ord(interp
, pat
, i
) == '%') { /* % */
332 substr
= Parrot_str_substr(interp
, pat
, old
, len
);
333 /* XXX This shouldn't modify targ the pointer */
334 targ
= Parrot_str_concat(interp
, targ
, substr
);
338 if (string_ord(interp
, pat
, i
+ 1) == '%') {
339 /* skip this one, make next the first char
340 * of literal sequence, starting at old */
347 /* hoo boy, here we go... */
349 HUGEINTVAL sharedint
= 0;
351 /* Storage for flags, etc. */
352 SpfInfo info
= { 0, 0, 0, 0, (PHASE
)0 };
354 /* Reset temporaries */
357 /* This can be really hard to understand, so I'll try to explain beforehand.
358 * A rough grammar for a printf format is:
360 * grammar Parrot::PrintF_Format {
362 * <other_stuff> (<field> <other_stuff>)*
380 * + # prefix with a + if necessary
384 * \# # 0, 0x on octal, hex; force decimal point on float
389 * [\d|\*]+ # minimum width
393 * [\d|\*]+ # width on integers;
394 * # number of digits after decimal on floats;
395 * # maximum width on strings
400 * h # short (or float)
402 * H # HUGEwhateverVAL (long [long]?, [long]? double)
406 * S # Parrot string (only with %s)
417 * X # hex with capital X (if #)
419 * B # binary with capital B (if #)
420 * u # unsigned integer
434 * Complication: once upon a time, %P existed. Now you should
435 * use %Ps, %Pd or %Pf, but we still need to support the old form.
436 * The same is true of %S--%Ss is the best form, but %S is still
439 * The implementation of Parrot_vsprintf is surprisingly similar to this
440 * regex, even though the two were developed semi-independently.
441 * Parrot_vsprintf keeps track of what it expects to see next (the
442 * 'phase')--flags, width, precision, size, or field type (term). If it
443 * doesn't find a character that fits whatever it's expecting, it sets
444 * info.phase to the next thing and tries it. The first four phases just
445 * set flags--the last does all the work.
448 for (++i
; i
< pat_len
&& info
.phase
!= PHASE_DONE
; ++i
) {
449 const INTVAL ch
= string_ord(interp
, pat
, i
);
451 switch (info
.phase
) {
452 /*@fallthrough@ */ case PHASE_FLAGS
:
455 info
.flags
|= FLAG_MINUS
;
459 info
.flags
|= FLAG_PLUS
;
463 info
.flags
|= FLAG_ZERO
;
467 info
.flags
|= FLAG_SPACE
;
471 info
.flags
|= FLAG_SHARP
;
475 info
.phase
= PHASE_WIDTH
;
479 /*@fallthrough@ */ case PHASE_WIDTH
:
491 info
.flags
|= FLAG_WIDTH
;
493 info
.width
+= ch
- '0';
497 info
.flags
|= FLAG_WIDTH
;
498 num
= obj
->getint(interp
, SIZE_XVAL
, obj
);
500 info
.flags
|= FLAG_MINUS
;
509 info
.phase
= PHASE_PREC
;
513 info
.phase
= PHASE_PREC
;
517 /*@fallthrough@ */ case PHASE_PREC
:
529 info
.flags
|= FLAG_PREC
;
531 info
.prec
+= ch
- '0';
535 info
.flags
|= FLAG_PREC
;
536 info
.prec
= (UINTVAL
)obj
->getint(interp
,
538 info
.phase
= PHASE_TYPE
;
542 info
.phase
= PHASE_TYPE
;
545 /*@fallthrough@ */ case PHASE_TYPE
:
548 info
.type
= SIZE_SHORT
;
552 info
.type
= SIZE_LONG
;
557 info
.type
= SIZE_HUGE
;
561 info
.type
= SIZE_XVAL
;
565 info
.type
= SIZE_OPCODE
;
569 info
.type
= SIZE_PMC
;
573 info
.type
= SIZE_PSTR
;
577 info
.phase
= PHASE_TERM
;
581 /*@fallthrough@ */ case PHASE_TERM
:
586 STRING
* const ts
= string_chr(interp
,
587 (UINTVAL
)obj
->getint(interp
, info
.type
, obj
));
588 targ
= str_concat_w_flags(interp
, targ
, &info
, ts
, NULL
);
594 const UHUGEINTVAL theuint
=
595 obj
->getuint(interp
, info
.type
, obj
);
597 Parrot_str_from_uint(interp
, tc
, theuint
, 8, 0);
598 STRING
* const prefix
= CONST_STRING(interp
, "0");
600 /* unsigned conversion - no plus */
601 info
.flags
&= ~FLAG_PLUS
;
602 targ
= str_concat_w_flags(interp
, targ
,
609 const UHUGEINTVAL theuint
=
610 obj
->getuint(interp
, info
.type
, obj
);
612 Parrot_str_from_uint(interp
, tc
, theuint
, 16, 0);
613 STRING
* const prefix
= CONST_STRING(interp
, "0x");
615 /* unsigned conversion - no plus */
616 info
.flags
&= ~FLAG_PLUS
;
617 targ
= str_concat_w_flags(interp
, targ
,
624 STRING
* const prefix
= CONST_STRING(interp
, "0X");
625 const UHUGEINTVAL theuint
=
626 obj
->getuint(interp
, info
.type
, obj
);
628 Parrot_str_from_uint(interp
, tc
, theuint
, 16, 0);
629 ts
= Parrot_str_upcase(interp
, ts
);
631 /* unsigned conversion - no plus */
632 info
.flags
&= ~FLAG_PLUS
;
633 targ
= str_concat_w_flags(interp
, targ
,
640 STRING
* const prefix
= CONST_STRING(interp
, "0b");
641 const UHUGEINTVAL theuint
=
642 obj
->getuint(interp
, info
.type
, obj
);
644 Parrot_str_from_uint(interp
, tc
, theuint
, 2, 0);
646 /* unsigned conversion - no plus */
647 info
.flags
&= ~FLAG_PLUS
;
648 targ
= str_concat_w_flags(interp
, targ
,
655 STRING
* const prefix
= CONST_STRING(interp
, "0B");
656 const HUGEINTVAL theint
=
657 obj
->getint(interp
, info
.type
, obj
);
659 Parrot_str_from_int_base(interp
, tc
, theint
, 2);
661 /* unsigned conversion - no plus */
662 info
.flags
&= ~FLAG_PLUS
;
663 targ
= str_concat_w_flags(interp
, targ
,
670 const UHUGEINTVAL theuint
=
671 obj
->getuint(interp
, info
.type
, obj
);
678 /* EVIL: Work around bug in glibc that makes %0lld
679 * sometimes output an empty string. */
680 if (!(info
.flags
& FLAG_WIDTH
))
681 info
.flags
&= ~FLAG_ZERO
;
683 sharedint
= obj
->getint(interp
, info
.type
, obj
);
687 gen_sprintf_call(tc
, &info
, ch
);
690 char * const tempstr
=
691 Parrot_str_to_cstring(interp
, ts
);
693 #ifdef PARROT_HAS_SNPRINTF
694 snprintf(tc
, PARROT_SPRINTF_BUFFER_SIZE
,
697 /* the buffer is 4096, so no problem here */
698 sprintf(tc
, tempstr
, sharedint
);
700 Parrot_str_free_cstring(tempstr
);
702 targ
= Parrot_str_concat(interp
, targ
, cstr2pstr(tc
));
708 STRING
* const prefix
= CONST_STRING(interp
, "0x");
709 const void * const ptr
=
710 obj
->getptr(interp
, info
.type
, obj
);
711 STRING
* const ts
= Parrot_str_from_uint(interp
, tc
,
712 (UHUGEINTVAL
) (size_t) ptr
, 16, 0);
714 targ
= str_concat_w_flags(interp
, targ
, &info
,
719 /* FLOATS - We cheat on these and use snprintf. */
727 const HUGEFLOATVAL thefloat
=
728 obj
->getfloat(interp
, info
.type
, obj
);
730 /* check for Inf and NaN values */
731 if (thefloat
== PARROT_FLOATVAL_INF_POSITIVE
) {
732 ts
= cstr2pstr(PARROT_CSTRING_INF_POSITIVE
);
734 else if (thefloat
== PARROT_FLOATVAL_INF_NEGATIVE
) {
735 ts
= cstr2pstr(PARROT_CSTRING_INF_NEGATIVE
);
737 else if (thefloat
!= thefloat
) {
738 ts
= cstr2pstr(PARROT_CSTRING_NAN_QUIET
);
741 /* turn -0.0 into 0.0 */
742 gen_sprintf_call(tc
, &info
, ch
);
746 /* XXX lost precision if %Hg or whatever */
748 char * const tempstr
=
749 Parrot_str_to_cstring(interp
, ts
);
751 #ifdef PARROT_HAS_SNPRINTF
752 snprintf(tc
, PARROT_SPRINTF_BUFFER_SIZE
,
756 /* the buffer is 4096, so no problem here */
757 sprintf(tc
, tempstr
, (double)thefloat
);
759 Parrot_str_free_cstring(tempstr
);
764 /* Microsoft defaults to three digits for
765 * exponents, even when fewer digits would suffice.
766 * For the sake of portability, we will here
767 * attempt to hide that. */
768 if (ch
== 'g' || ch
== 'G'
769 || ch
== 'e' || ch
== 'E') {
770 const size_t tclen
= strlen(tc
);
772 for (j
= 0; j
< tclen
; ++j
) {
773 if ((tc
[j
] == 'e' || tc
[j
] == 'E')
774 && (tc
[j
+1] == '+' || tc
[j
+1] == '-')
776 && isdigit((unsigned char)tc
[j
+3])
777 && isdigit((unsigned char)tc
[j
+4]))
779 mem_sys_memmove(&tc
[j
+2], &tc
[j
+3],
782 /* now fix any broken length */
784 if ((info
.flags
& FLAG_WIDTH
)
785 && strlen(tc
) < info
.width
) {
786 if (info
.flags
& FLAG_MINUS
)
789 mem_sys_memmove(&tc
[1], &tc
[0],
791 tc
[0] = (info
.flags
& FLAG_ZERO
) ? '0' : ' ';
795 /* only one fix required per string */
802 targ
= Parrot_str_concat(interp
, targ
, cstr2pstr(tc
));
807 case 'r': /* Python repr */
808 /* XXX the right fix is to add a getrepr entry *
809 * to SPRINTF_OBJ, but for now, getstring_pmc *
810 * is inlined and modified to call get_repr */
811 if (obj
->getstring
== pmc_core
.getstring
) {
813 VTABLE_get_pmc_keyed_int(interp
,
817 STRING
* const string
= (VTABLE_get_repr(interp
, tmp
));
818 STRING
* const ts
= handle_flags(interp
, &info
,
822 targ
= Parrot_str_concat(interp
, targ
, ts
);
829 STRING
* const string
= obj
->getstring(interp
,
831 /* XXX Silently ignore? */
832 if (!STRING_IS_NULL(string
)) {
833 STRING
* const ts
= handle_flags(interp
,
834 &info
, string
, 0, NULL
);
835 targ
= Parrot_str_concat(interp
, targ
, ts
);
841 /* fake the old %P and %S commands */
842 if (info
.type
== SIZE_PMC
843 || info
.type
== SIZE_PSTR
) {
846 /* case 's' will see the SIZE_PMC or SIZE_PSTR
847 * and assume it was %Ps (or %Ss). Genius,
851 Parrot_ex_throw_from_c_args(interp
, NULL
,
852 EXCEPTION_INVALID_CHARACTER
,
853 "'%c' is not a valid sprintf format", ch
);
857 info
.phase
= PHASE_DONE
;
862 /* This is the terminating condition of the surrounding
865 PANIC(interp
, "We can't be here");
878 substr
= Parrot_str_substr(interp
, pat
, old
, len
);
879 targ
= Parrot_str_concat(interp
, targ
, substr
);
891 F<src/misc.h>, F<src/misc.c>, F<src/spf_vtable.c>.
900 * c-file-style: "parrot"
902 * vim: expandtab shiftwidth=4: