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 "parrot/string_funcs.h"
26 #include "spf_render.str"
37 typedef struct SpfInfo_tag
{
55 /* HEADERIZER HFILE: include/parrot/misc.h */
57 /* HEADERIZER BEGIN: static */
58 /* Don't modify between HEADERIZER BEGIN / HEADERIZER END. Your changes will be lost. */
60 static void gen_sprintf_call(
62 ARGMOD(SpfInfo
*info
),
64 __attribute__nonnull__(1)
65 __attribute__nonnull__(2)
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)
80 PARROT_CANNOT_RETURN_NULL
81 static STRING
* str_append_w_flags(PARROT_INTERP
,
83 ARGIN(const SpfInfo
*info
),
85 ARGIN_NULLOK(STRING
*prefix
))
86 __attribute__nonnull__(1)
87 __attribute__nonnull__(2)
88 __attribute__nonnull__(3)
89 __attribute__nonnull__(4)
93 #define ASSERT_ARGS_gen_sprintf_call __attribute__unused__ int _ASSERT_ARGS_CHECK = \
94 PARROT_ASSERT_ARG(out) \
95 || PARROT_ASSERT_ARG(info)
96 #define ASSERT_ARGS_handle_flags __attribute__unused__ int _ASSERT_ARGS_CHECK = \
97 PARROT_ASSERT_ARG(interp) \
98 || PARROT_ASSERT_ARG(info) \
99 || PARROT_ASSERT_ARG(str)
100 #define ASSERT_ARGS_str_append_w_flags __attribute__unused__ int _ASSERT_ARGS_CHECK = \
101 PARROT_ASSERT_ARG(interp) \
102 || PARROT_ASSERT_ARG(dest) \
103 || PARROT_ASSERT_ARG(info) \
104 || PARROT_ASSERT_ARG(src)
105 /* Don't modify between HEADERIZER BEGIN / HEADERIZER END. Your changes will be lost. */
106 /* HEADERIZER END: static */
109 /* Per Dan's orders, we will not use sprintf if snprintf isn't
113 # define snprintf _snprintf
118 =item C<static STRING * handle_flags(PARROT_INTERP, const SpfInfo *info, STRING
119 *str, INTVAL is_int_type, STRING* prefix)>
121 Handles C<+>, C<->, C<0>, C<#>, space, width, and prec.
127 PARROT_CANNOT_RETURN_NULL
129 handle_flags(PARROT_INTERP
, ARGIN(const SpfInfo
*info
), ARGMOD(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 Parrot_str_chopn_inplace(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
, 0);
149 else if (info
->flags
& FLAG_SPACE
) {
150 STRING
* const cs
= CONST_STRING(interp
, " ");
151 str
= Parrot_str_concat(interp
, cs
, str
, 0);
157 if ((info
->flags
& FLAG_SHARP
) && prefix
) {
158 str
= Parrot_str_concat(interp
, prefix
, str
, 0);
159 len
+= Parrot_str_byte_length(interp
, prefix
);
161 /* XXX sharp + fill ??? */
164 /* precision - only for floats, which is handled elsewhere */
165 if (info
->flags
& FLAG_PREC
) {
166 info
->flags
|= FLAG_WIDTH
;
167 if (string_ord(interp
, str
, 0) == '-' ||
168 string_ord(interp
, str
, 0) == '+') {
169 info
->width
= info
->prec
+ 1;
172 info
->width
= info
->prec
;
178 /* string precision */
179 if (info
->flags
& FLAG_PREC
&& info
->prec
== 0) {
180 Parrot_str_chopn_inplace(interp
, str
, len
);
183 else if (info
->flags
& FLAG_PREC
&& info
->prec
< len
) {
184 Parrot_str_chopn_inplace(interp
, str
, -(INTVAL
)(info
->prec
));
189 if ((info
->flags
& FLAG_WIDTH
) && info
->width
> len
) {
190 STRING
* const filler
=
191 ((info
->flags
& FLAG_ZERO
) && !(info
->flags
& FLAG_MINUS
))
192 ? CONST_STRING(interp
, "0")
193 : CONST_STRING(interp
, " ");
194 STRING
* const fill
= Parrot_str_repeat(interp
, filler
, info
->width
- len
);
196 if (info
->flags
& FLAG_MINUS
) { /* left-align */
197 str
= Parrot_str_concat(interp
, str
, fill
, 0);
199 else { /* right-align */
200 /* signed and zero padded */
201 if (info
->flags
& FLAG_ZERO
202 && (string_ord(interp
, str
, 0) == '-' ||
203 string_ord(interp
, str
, 0) == '+')) {
206 ignored
= Parrot_str_substr(interp
, str
, 1, len
-1, &temp
, 0);
208 Parrot_str_chopn_inplace(interp
, str
, -1);
209 str
= Parrot_str_append(interp
, str
, fill
);
210 str
= Parrot_str_append(interp
, str
, temp
);
213 str
= Parrot_str_concat(interp
, fill
, str
, 0);
222 =item C<static STRING* str_append_w_flags(PARROT_INTERP, STRING *dest, const
223 SpfInfo *info, STRING *src, STRING *prefix)>
225 Used by Parrot_sprintf_format. Prepends supplied prefix for numeric
226 values. (e.g. 0x for hex.)
228 Returns the pointer to the modified string.
234 PARROT_CANNOT_RETURN_NULL
236 str_append_w_flags(PARROT_INTERP
, ARGOUT(STRING
*dest
), ARGIN(const SpfInfo
*info
),
237 ARGMOD(STRING
*src
), ARGIN_NULLOK(STRING
*prefix
))
239 ASSERT_ARGS(str_append_w_flags
)
240 src
= handle_flags(interp
, info
, src
, 1, prefix
);
241 dest
= Parrot_str_append(interp
, dest
, src
);
247 =item C<static void gen_sprintf_call(char *out, SpfInfo *info, int thingy)>
249 Turn the info structure back into an sprintf format. Far from being
250 pointless, this is used to call C<snprintf()> when we're confronted with
258 gen_sprintf_call(ARGOUT(char *out
), ARGMOD(SpfInfo
*info
), int thingy
)
260 ASSERT_ARGS(gen_sprintf_call
)
262 const int flags
= info
->flags
;
267 if (flags
& FLAG_MINUS
)
270 if (flags
& FLAG_PLUS
)
273 if (flags
& FLAG_ZERO
)
276 if (flags
& FLAG_SPACE
)
279 if (flags
& FLAG_SHARP
)
282 if (flags
& FLAG_WIDTH
) {
283 if (info
->width
> PARROT_SPRINTF_BUFFER_SIZE
- 1)
284 info
->width
= PARROT_SPRINTF_BUFFER_SIZE
;
286 p
+= sprintf(p
, "%u", (unsigned)info
->width
);
289 if (flags
& FLAG_PREC
) {
290 if (info
->prec
> PARROT_SPRINTF_MAX_PREC
)
291 info
->prec
= PARROT_SPRINTF_MAX_PREC
;
294 p
+= sprintf(p
, "%u", (unsigned)info
->prec
);
298 if (thingy
== 'd' || thingy
== 'i' ||thingy
== 'u') {
299 /* the u?int isa HUGEU?INTVAL aka long long
300 * the 'll' modifier is specced in susv3 - hopefully all our
301 * compilers support it too */
313 =item C<STRING * Parrot_sprintf_format(PARROT_INTERP, STRING *pat, SPRINTF_OBJ
316 This is the engine that does all the formatting.
322 PARROT_WARN_UNUSED_RESULT
323 PARROT_CANNOT_RETURN_NULL
325 Parrot_sprintf_format(PARROT_INTERP
,
326 ARGIN(STRING
*pat
), ARGIN(SPRINTF_OBJ
*obj
))
328 ASSERT_ARGS(Parrot_sprintf_format
)
332 INTVAL pat_len
= (INTVAL
)Parrot_str_byte_length(interp
, pat
);
335 /* start with a buffer; double the pattern length to avoid realloc #1 */
336 STRING
*targ
= Parrot_str_new_noinit(interp
, enum_stringrep_one
, pat_len
* 2);
338 /* ts is used almost universally as an intermediate target;
339 * tc is used as a temporary buffer by Parrot_str_from_uint and
340 * as a target by gen_sprintf_call.
342 STRING
*substr
= NULL
;
343 char tc
[PARROT_SPRINTF_BUFFER_SIZE
];
345 for (i
= 0; i
< pat_len
; i
++) {
346 if (string_ord(interp
, pat
, i
) == '%') { /* % */
349 = Parrot_str_substr(interp
, pat
, old
, len
, &substr
, 1);
351 /* XXX This shouldn't modify targ the pointer */
352 targ
= Parrot_str_append(interp
, targ
, substr
);
356 if (string_ord(interp
, pat
, i
+ 1) == '%') {
357 /* skip this one, make next the first char
358 * of literal sequence, starting at old */
365 /* hoo boy, here we go... */
367 HUGEINTVAL sharedint
= 0;
369 /* Storage for flags, etc. */
370 SpfInfo info
= { 0, 0, 0, 0, (PHASE
)0 };
372 /* Reset temporaries */
375 /* This can be really hard to understand, so I'll try to explain beforehand.
376 * A rough grammar for a printf format is:
378 * grammar Parrot::PrintF_Format {
380 * <other_stuff> (<field> <other_stuff>)*
398 * + # prefix with a + if necessary
402 * \# # 0, 0x on octal, hex; force decimal point on float
407 * [\d|\*]+ # minimum width
411 * [\d|\*]+ # width on integers;
412 * # number of digits after decimal on floats;
413 * # maximum width on strings
418 * h # short (or float)
420 * H # HUGEwhateverVAL (long [long]?, [long]? double)
424 * S # Parrot string (only with %s)
435 * X # hex with capital X (if #)
437 * B # binary with capital B (if #)
438 * u # unsigned integer
452 * Complication: once upon a time, %P existed. Now you should
453 * use %Ps, %Pd or %Pf, but we still need to support the old form.
454 * The same is true of %S--%Ss is the best form, but %S is still
457 * The implementation of Parrot_vsprintf is surprisingly similar to this
458 * regex, even though the two were developed semi-independently.
459 * Parrot_vsprintf keeps track of what it expects to see next (the
460 * 'phase')--flags, width, precision, size, or field type (term). If it
461 * doesn't find a character that fits whatever it's expecting, it sets
462 * info.phase to the next thing and tries it. The first four phases just
463 * set flags--the last does all the work.
466 for (i
++; i
< pat_len
&& info
.phase
!= PHASE_DONE
; i
++) {
467 const INTVAL ch
= string_ord(interp
, pat
, i
);
469 switch (info
.phase
) {
470 /*@fallthrough@ */ case PHASE_FLAGS
:
473 info
.flags
|= FLAG_MINUS
;
477 info
.flags
|= FLAG_PLUS
;
481 info
.flags
|= FLAG_ZERO
;
485 info
.flags
|= FLAG_SPACE
;
489 info
.flags
|= FLAG_SHARP
;
493 info
.phase
= PHASE_WIDTH
;
497 /*@fallthrough@ */ case PHASE_WIDTH
:
509 info
.flags
|= FLAG_WIDTH
;
511 info
.width
+= ch
- '0';
515 info
.flags
|= FLAG_WIDTH
;
516 num
= obj
->getint(interp
, SIZE_XVAL
, obj
);
518 info
.flags
|= FLAG_MINUS
;
527 info
.phase
= PHASE_PREC
;
531 info
.phase
= PHASE_PREC
;
535 /*@fallthrough@ */ case PHASE_PREC
:
547 info
.flags
|= FLAG_PREC
;
549 info
.prec
+= ch
- '0';
553 info
.flags
|= FLAG_PREC
;
554 info
.prec
= (UINTVAL
)obj
->getint(interp
,
556 info
.phase
= PHASE_TYPE
;
560 info
.phase
= PHASE_TYPE
;
563 /*@fallthrough@ */ case PHASE_TYPE
:
566 info
.type
= SIZE_SHORT
;
570 info
.type
= SIZE_LONG
;
575 info
.type
= SIZE_HUGE
;
579 info
.type
= SIZE_XVAL
;
583 info
.type
= SIZE_OPCODE
;
587 info
.type
= SIZE_PMC
;
591 info
.type
= SIZE_PSTR
;
595 info
.phase
= PHASE_TERM
;
599 /*@fallthrough@ */ case PHASE_TERM
:
604 STRING
* const ts
= string_chr(interp
,
605 (UINTVAL
)obj
->getint(interp
, info
.type
, obj
));
606 targ
= str_append_w_flags(interp
, targ
, &info
, ts
, NULL
);
612 const UHUGEINTVAL theuint
=
613 obj
->getuint(interp
, info
.type
, obj
);
615 Parrot_str_from_uint(interp
, tc
, theuint
, 8, 0);
616 STRING
* const prefix
= CONST_STRING(interp
, "0");
618 /* unsigned conversion - no plus */
619 info
.flags
&= ~FLAG_PLUS
;
620 targ
= str_append_w_flags(interp
, targ
,
627 const UHUGEINTVAL theuint
=
628 obj
->getuint(interp
, info
.type
, obj
);
630 Parrot_str_from_uint(interp
, tc
, theuint
, 16, 0);
631 STRING
* const prefix
= CONST_STRING(interp
, "0x");
633 /* unsigned conversion - no plus */
634 info
.flags
&= ~FLAG_PLUS
;
635 targ
= str_append_w_flags(interp
, targ
,
642 STRING
* const prefix
= CONST_STRING(interp
, "0X");
643 const UHUGEINTVAL theuint
=
644 obj
->getuint(interp
, info
.type
, obj
);
646 Parrot_str_from_uint(interp
, tc
, theuint
, 16, 0);
647 Parrot_str_upcase_inplace(interp
, ts
);
649 /* unsigned conversion - no plus */
650 info
.flags
&= ~FLAG_PLUS
;
651 targ
= str_append_w_flags(interp
, targ
,
658 STRING
* const prefix
= CONST_STRING(interp
, "0b");
659 const UHUGEINTVAL theuint
=
660 obj
->getuint(interp
, info
.type
, obj
);
662 Parrot_str_from_uint(interp
, tc
, theuint
, 2, 0);
664 /* unsigned conversion - no plus */
665 info
.flags
&= ~FLAG_PLUS
;
666 targ
= str_append_w_flags(interp
, targ
,
673 STRING
* const prefix
= CONST_STRING(interp
, "0B");
674 const HUGEINTVAL theint
=
675 obj
->getint(interp
, info
.type
, obj
);
677 Parrot_str_from_int_base(interp
, tc
, theint
, 2);
679 /* unsigned conversion - no plus */
680 info
.flags
&= ~FLAG_PLUS
;
681 targ
= str_append_w_flags(interp
, targ
,
688 const UHUGEINTVAL theuint
=
689 obj
->getuint(interp
, info
.type
, obj
);
696 /* EVIL: Work around bug in glibc that makes %0lld
697 * sometimes output an empty string. */
698 if (!(info
.flags
& FLAG_WIDTH
))
699 info
.flags
&= ~FLAG_ZERO
;
701 sharedint
= obj
->getint(interp
, info
.type
, obj
);
705 gen_sprintf_call(tc
, &info
, ch
);
708 char * const tempstr
=
709 Parrot_str_to_cstring(interp
, ts
);
711 #ifdef PARROT_HAS_SNPRINTF
712 snprintf(tc
, PARROT_SPRINTF_BUFFER_SIZE
,
715 /* the buffer is 4096, so no problem here */
716 sprintf(tc
, tempstr
, sharedint
);
718 Parrot_str_free_cstring(tempstr
);
720 targ
= Parrot_str_append(interp
, targ
, cstr2pstr(tc
));
726 STRING
* const prefix
= CONST_STRING(interp
, "0x");
727 const void * const ptr
=
728 obj
->getptr(interp
, info
.type
, obj
);
729 STRING
* const ts
= Parrot_str_from_uint(interp
, tc
,
730 (UHUGEINTVAL
) (size_t) ptr
, 16, 0);
732 targ
= str_append_w_flags(interp
, targ
, &info
,
737 /* FLOATS - We cheat on these and use snprintf. */
745 const HUGEFLOATVAL thefloat
=
746 obj
->getfloat(interp
, info
.type
, obj
);
748 /* check for Inf and NaN values */
749 if (thefloat
== PARROT_FLOATVAL_INF_POSITIVE
) {
750 ts
= cstr2pstr(PARROT_CSTRING_INF_POSITIVE
);
752 else if (thefloat
== PARROT_FLOATVAL_INF_NEGATIVE
) {
753 ts
= cstr2pstr(PARROT_CSTRING_INF_NEGATIVE
);
755 else if (thefloat
!= thefloat
) {
756 ts
= cstr2pstr(PARROT_CSTRING_NAN_QUIET
);
759 /* turn -0.0 into 0.0 */
760 gen_sprintf_call(tc
, &info
, ch
);
764 /* XXX lost precision if %Hg or whatever */
766 char * const tempstr
=
767 Parrot_str_to_cstring(interp
, ts
);
769 #ifdef PARROT_HAS_SNPRINTF
770 snprintf(tc
, PARROT_SPRINTF_BUFFER_SIZE
,
774 /* the buffer is 4096, so no problem here */
775 sprintf(tc
, tempstr
, (double)thefloat
);
777 Parrot_str_free_cstring(tempstr
);
782 /* Microsoft defaults to three digits for
783 * exponents, even when fewer digits would suffice.
784 * For the sake of portability, we will here
785 * attempt to hide that. */
786 if (ch
== 'g' || ch
== 'G'
787 || ch
== 'e' || ch
== 'E') {
788 const size_t tclen
= strlen(tc
);
790 for (j
= 0; j
< tclen
; j
++) {
791 if ((tc
[j
] == 'e' || tc
[j
] == 'E')
792 && (tc
[j
+1] == '+' || tc
[j
+1] == '-')
794 && isdigit((unsigned char)tc
[j
+3])
795 && isdigit((unsigned char)tc
[j
+4]))
797 mem_sys_memmove(&tc
[j
+2], &tc
[j
+3],
800 /* now fix any broken length */
802 if ((info
.flags
& FLAG_WIDTH
)
803 && strlen(tc
) < info
.width
) {
804 if (info
.flags
& FLAG_MINUS
)
807 mem_sys_memmove(&tc
[1], &tc
[0],
809 tc
[0] = (info
.flags
& FLAG_ZERO
) ? '0' : ' ';
813 /* only one fix required per string */
820 targ
= Parrot_str_append(interp
, targ
, cstr2pstr(tc
));
825 case 'r': /* Python repr */
826 /* XXX the right fix is to add a getrepr entry *
827 * to SPRINTF_OBJ, but for now, getstring_pmc *
828 * is inlined and modified to call get_repr */
829 if (obj
->getstring
== pmc_core
.getstring
) {
831 VTABLE_get_pmc_keyed_int(interp
,
835 STRING
* const string
= (VTABLE_get_repr(interp
, tmp
));
836 STRING
* const ts
= handle_flags(interp
, &info
,
840 targ
= Parrot_str_append(interp
, targ
, ts
);
847 STRING
* const string
= obj
->getstring(interp
,
849 /* XXX Silently ignore? */
850 if (!STRING_IS_NULL(string
)) {
851 STRING
* const ts
= handle_flags(interp
,
852 &info
, string
, 0, NULL
);
853 targ
= Parrot_str_append(interp
, targ
, ts
);
859 /* fake the old %P and %S commands */
860 if (info
.type
== SIZE_PMC
861 || info
.type
== SIZE_PSTR
) {
864 /* case 's' will see the SIZE_PMC or SIZE_PSTR
865 * and assume it was %Ps (or %Ss). Genius,
869 Parrot_ex_throw_from_c_args(interp
, NULL
,
870 EXCEPTION_INVALID_CHARACTER
,
871 "'%c' is not a valid sprintf format", ch
);
875 info
.phase
= PHASE_DONE
;
880 /* This is the terminating condition of the surrounding
883 PANIC(interp
, "We can't be here");
896 STRING
*ignored
= Parrot_str_substr(interp
, pat
, old
, len
, &substr
, 1);
898 targ
= Parrot_str_append(interp
, targ
, substr
);
910 F<src/misc.h>, F<src/misc.c>, F<src/spf_vtable.c>.
919 * c-file-style: "parrot"
921 * vim: expandtab shiftwidth=4: