[t][cage] Remove PGE-dependence from t/op/inf_nan.t since it is part of 'make coretest'
[parrot.git] / src / spf_render.c
blobf6406f4f012d73726fa71e93ae9a9b63dc80f7f7
1 /*
2 Copyright (C) 2001-2009, Parrot Foundation.
3 $Id$
5 =head1 NAME
7 src/spf_render.c - Parrot sprintf
9 =head1 DESCRIPTION
11 Implements the main function that drives the C<Parrot_sprintf> family
12 and its utility functions.
14 =head2 Utility Functions
16 =over 4
18 =cut
22 #define IN_SPF_SYSTEM
24 #include "parrot/parrot.h"
25 #include "parrot/string_funcs.h"
26 #include "spf_render.str"
28 typedef enum {
29 PHASE_FLAGS = 0,
30 PHASE_WIDTH,
31 PHASE_PREC,
32 PHASE_TYPE,
33 PHASE_TERM,
34 PHASE_DONE
35 } PHASE;
37 typedef struct SpfInfo_tag {
38 UINTVAL width;
39 UINTVAL prec;
40 INTVAL flags;
41 INTVAL type;
42 PHASE phase;
43 } SpfInfo;
45 enum {
46 FLAG_MINUS = (1<<0),
47 FLAG_PLUS = (1<<1),
48 FLAG_ZERO = (1<<2),
49 FLAG_SPACE = (1<<3),
50 FLAG_SHARP = (1<<4),
51 FLAG_WIDTH = (1<<5),
52 FLAG_PREC = (1<<6)
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(
61 ARGOUT(char *out),
62 ARGMOD(SpfInfo *info),
63 int thingy)
64 __attribute__nonnull__(1)
65 __attribute__nonnull__(2)
66 FUNC_MODIFIES(*out)
67 FUNC_MODIFIES(*info);
69 PARROT_CANNOT_RETURN_NULL
70 static STRING * handle_flags(PARROT_INTERP,
71 ARGIN(const SpfInfo *info),
72 ARGMOD(STRING *str),
73 INTVAL is_int_type,
74 ARGIN_NULLOK(STRING* prefix))
75 __attribute__nonnull__(1)
76 __attribute__nonnull__(2)
77 __attribute__nonnull__(3)
78 FUNC_MODIFIES(*str);
80 PARROT_CANNOT_RETURN_NULL
81 static STRING* str_append_w_flags(PARROT_INTERP,
82 ARGOUT(STRING *dest),
83 ARGIN(const SpfInfo *info),
84 ARGMOD(STRING *src),
85 ARGIN_NULLOK(STRING *prefix))
86 __attribute__nonnull__(1)
87 __attribute__nonnull__(2)
88 __attribute__nonnull__(3)
89 __attribute__nonnull__(4)
90 FUNC_MODIFIES(*dest)
91 FUNC_MODIFIES(*src);
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
110 * around for us.
112 #ifdef _MSC_VER
113 # define snprintf _snprintf
114 #endif
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.
123 =cut
127 PARROT_CANNOT_RETURN_NULL
128 static STRING *
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);
135 if (is_int_type) {
136 if (info->flags & FLAG_PREC && info->prec == 0 &&
137 len == 1 &&
138 string_ord(interp, str, 0) == '0') {
139 Parrot_str_chopn_inplace(interp, str, len);
140 len = 0;
142 /* +, space */
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);
147 len++;
149 else if (info->flags & FLAG_SPACE) {
150 STRING * const cs = CONST_STRING(interp, " ");
151 str = Parrot_str_concat(interp, cs, str, 0);
152 len++;
156 /* # 0x ... */
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 ??? */
163 #if 0
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;
171 else {
172 info->width = info->prec;
175 #endif
177 else {
178 /* string precision */
179 if (info->flags & FLAG_PREC && info->prec == 0) {
180 Parrot_str_chopn_inplace(interp, str, len);
181 len = 0;
183 else if (info->flags & FLAG_PREC && info->prec < len) {
184 Parrot_str_chopn_inplace(interp, str, -(INTVAL)(info->prec));
185 len = 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) == '+')) {
204 STRING *temp = NULL;
205 STRING *ignored;
206 ignored = Parrot_str_substr(interp, str, 1, len-1, &temp, 0);
207 UNUSED(ignored);
208 Parrot_str_chopn_inplace(interp, str, -1);
209 str = Parrot_str_append(interp, str, fill);
210 str = Parrot_str_append(interp, str, temp);
212 else {
213 str = Parrot_str_concat(interp, fill, str, 0);
217 return str;
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.
230 =cut
234 PARROT_CANNOT_RETURN_NULL
235 static STRING*
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);
242 return dest;
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
251 a float.
253 =cut
257 static void
258 gen_sprintf_call(ARGOUT(char *out), ARGMOD(SpfInfo *info), int thingy)
260 ASSERT_ARGS(gen_sprintf_call)
262 const int flags = info->flags;
263 char *p = out;
264 *p++ = '%';
266 if (flags) {
267 if (flags & FLAG_MINUS)
268 *p++ = '-';
270 if (flags & FLAG_PLUS)
271 *p++ = '+';
273 if (flags & FLAG_ZERO)
274 *p++ = '0';
276 if (flags & FLAG_SPACE)
277 *p++ = ' ';
279 if (flags & FLAG_SHARP)
280 *p++ = '#';
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;
293 *p++ = '.';
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 */
302 *p++ = 'l';
303 *p++ = 'l';
306 *p++ = (char)thingy;
307 *p = '\0';
313 =item C<STRING * Parrot_sprintf_format(PARROT_INTERP, STRING *pat, SPRINTF_OBJ
314 *obj)>
316 This is the engine that does all the formatting.
318 =cut
322 PARROT_WARN_UNUSED_RESULT
323 PARROT_CANNOT_RETURN_NULL
324 STRING *
325 Parrot_sprintf_format(PARROT_INTERP,
326 ARGIN(STRING *pat), ARGIN(SPRINTF_OBJ *obj))
328 ASSERT_ARGS(Parrot_sprintf_format)
329 INTVAL i;
330 INTVAL len = 0;
331 INTVAL old = 0;
332 INTVAL pat_len = (INTVAL)Parrot_str_byte_length(interp, pat);
333 HUGEINTVAL num;
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) == '%') { /* % */
347 if (len) {
348 STRING *ignored
349 = Parrot_str_substr(interp, pat, old, len, &substr, 1);
350 UNUSED(ignored);
351 /* XXX This shouldn't modify targ the pointer */
352 targ = Parrot_str_append(interp, targ, substr);
354 len = 0;
355 old = i;
356 if (string_ord(interp, pat, i + 1) == '%') {
357 /* skip this one, make next the first char
358 * of literal sequence, starting at old */
359 i++;
360 old++;
361 len++;
362 continue;
364 else {
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 */
373 tc[0] = '\0';
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 {
379 * rule format {
380 * <other_stuff> (<field> <other_stuff>)*
383 * rule other_stuff {
384 * [<[^\%]> | \%\%]*:
387 * rule field {
388 * \%
389 * <flags>?
390 * <width>?
391 * [\.<prec>]?
392 * <size>?
393 * <term>
396 * rule flags {
397 * <[
398 * + # prefix with a + if necessary
399 * - # left-align
400 * 0 # zero-pad
401 * <sp> # space-pad
402 * \# # 0, 0x on octal, hex; force decimal point on float
403 * ]>+
406 * rule width {
407 * [\d|\*]+ # minimum width
410 * rule prec {
411 * [\d|\*]+ # width on integers;
412 * # number of digits after decimal on floats;
413 * # maximum width on strings
416 * rule size {
417 * <[
418 * h # short (or float)
419 * l # long
420 * H # HUGEwhateverVAL (long [long]?, [long]? double)
421 * v # whateverVAL
422 * O # opcode_t
423 * P # really a PMC
424 * S # Parrot string (only with %s)
425 * ]>
428 * rule term {
429 * <[
430 * c # char
431 * d # integer
432 * i # integer
433 * o # octal
434 * x # hex
435 * X # hex with capital X (if #)
436 * b # binary
437 * B # binary with capital B (if #)
438 * u # unsigned integer
439 * p # pointer
441 * e # 1e1
442 * E # 1E1
443 * f # 1.0
444 * g # 1, 0.1, 1e1
445 * G # 1, 0.1, 1E1
447 * s # string
448 * ]>
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
455 * supported.
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:
471 switch (ch) {
472 case '-':
473 info.flags |= FLAG_MINUS;
474 continue;
476 case '+':
477 info.flags |= FLAG_PLUS;
478 continue;
480 case '0':
481 info.flags |= FLAG_ZERO;
482 continue;
484 case ' ':
485 info.flags |= FLAG_SPACE;
486 continue;
488 case '#':
489 info.flags |= FLAG_SHARP;
490 continue;
492 default:
493 info.phase = PHASE_WIDTH;
497 /*@fallthrough@ */ case PHASE_WIDTH:
498 switch (ch) {
499 case '0':
500 case '1':
501 case '2':
502 case '3':
503 case '4':
504 case '5':
505 case '6':
506 case '7':
507 case '8':
508 case '9':
509 info.flags |= FLAG_WIDTH;
510 info.width *= 10;
511 info.width += ch - '0';
512 continue;
514 case '*':
515 info.flags |= FLAG_WIDTH;
516 num = obj->getint(interp, SIZE_XVAL, obj);
517 if (num < 0) {
518 info.flags |= FLAG_MINUS;
519 info.width = -num;
521 else {
522 info.width = num;
524 continue;
526 case '.':
527 info.phase = PHASE_PREC;
528 continue;
530 default:
531 info.phase = PHASE_PREC;
535 /*@fallthrough@ */ case PHASE_PREC:
536 switch (ch) {
537 case '0':
538 case '1':
539 case '2':
540 case '3':
541 case '4':
542 case '5':
543 case '6':
544 case '7':
545 case '8':
546 case '9':
547 info.flags |= FLAG_PREC;
548 info.prec *= 10;
549 info.prec += ch - '0';
550 continue;
552 case '*':
553 info.flags |= FLAG_PREC;
554 info.prec = (UINTVAL)obj->getint(interp,
555 SIZE_XVAL, obj);
556 info.phase = PHASE_TYPE;
557 continue;
559 default:
560 info.phase = PHASE_TYPE;
563 /*@fallthrough@ */ case PHASE_TYPE:
564 switch (ch) {
565 case 'h':
566 info.type = SIZE_SHORT;
567 continue;
569 case 'l':
570 info.type = SIZE_LONG;
571 continue;
573 case 'L':
574 case 'H':
575 info.type = SIZE_HUGE;
576 continue;
578 case 'v':
579 info.type = SIZE_XVAL;
580 continue;
582 case 'O':
583 info.type = SIZE_OPCODE;
584 continue;
586 case 'P':
587 info.type = SIZE_PMC;
588 continue;
590 case 'S':
591 info.type = SIZE_PSTR;
592 continue;
594 default:
595 info.phase = PHASE_TERM;
599 /*@fallthrough@ */ case PHASE_TERM:
600 switch (ch) {
601 /* INTEGERS */
602 case 'c':
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);
608 break;
610 case 'o':
612 const UHUGEINTVAL theuint =
613 obj->getuint(interp, info.type, obj);
614 STRING * const ts =
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,
621 &info, ts, prefix);
623 break;
625 case 'x':
627 const UHUGEINTVAL theuint =
628 obj->getuint(interp, info.type, obj);
629 STRING * const ts =
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,
636 &info, ts, prefix);
638 break;
640 case 'X':
642 STRING * const prefix = CONST_STRING(interp, "0X");
643 const UHUGEINTVAL theuint =
644 obj->getuint(interp, info.type, obj);
645 STRING * const ts =
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,
652 &info, ts, prefix);
654 break;
656 case 'b':
658 STRING * const prefix = CONST_STRING(interp, "0b");
659 const UHUGEINTVAL theuint =
660 obj->getuint(interp, info.type, obj);
661 STRING * const ts =
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,
667 &info, ts, prefix);
669 break;
671 case 'B':
673 STRING * const prefix = CONST_STRING(interp, "0B");
674 const HUGEINTVAL theint =
675 obj->getint(interp, info.type, obj);
676 STRING * const ts =
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,
682 &info, ts, prefix);
684 break;
686 case 'u':
688 const UHUGEINTVAL theuint =
689 obj->getuint(interp, info.type, obj);
690 sharedint = theuint;
692 goto do_sprintf;
693 case 'd':
694 case 'i':
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);
702 do_sprintf:
704 STRING *ts;
705 gen_sprintf_call(tc, &info, ch);
706 ts = cstr2pstr(tc);
708 char * const tempstr =
709 Parrot_str_to_cstring(interp, ts);
711 #ifdef PARROT_HAS_SNPRINTF
712 snprintf(tc, PARROT_SPRINTF_BUFFER_SIZE,
713 tempstr, sharedint);
714 #else
715 /* the buffer is 4096, so no problem here */
716 sprintf(tc, tempstr, sharedint);
717 #endif
718 Parrot_str_free_cstring(tempstr);
720 targ = Parrot_str_append(interp, targ, cstr2pstr(tc));
722 break;
724 case 'p':
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,
733 ts, prefix);
735 break;
737 /* FLOATS - We cheat on these and use snprintf. */
738 case 'e':
739 case 'E':
740 case 'f':
741 case 'g':
742 case 'G':
744 STRING *ts;
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);
758 else {
759 /* turn -0.0 into 0.0 */
760 gen_sprintf_call(tc, &info, ch);
761 ts = cstr2pstr(tc);
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,
771 tempstr,
772 (double)thefloat);
773 #else
774 /* the buffer is 4096, so no problem here */
775 sprintf(tc, tempstr, (double)thefloat);
776 #endif
777 Parrot_str_free_cstring(tempstr);
780 #ifdef WIN32
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);
789 size_t j;
790 for (j = 0; j < tclen; j++) {
791 if ((tc[j] == 'e' || tc[j] == 'E')
792 && (tc[j+1] == '+' || tc[j+1] == '-')
793 && tc[j+2] == '0'
794 && isdigit((unsigned char)tc[j+3])
795 && isdigit((unsigned char)tc[j+4]))
797 mem_sys_memmove(&tc[j+2], &tc[j+3],
798 strlen(&tc[j+2]));
800 /* now fix any broken length */
802 if ((info.flags & FLAG_WIDTH)
803 && strlen(tc) < info.width) {
804 if (info.flags & FLAG_MINUS)
805 strcat(tc, " ");
806 else {
807 mem_sys_memmove(&tc[1], &tc[0],
808 strlen(tc) + 1);
809 tc[0] = (info.flags & FLAG_ZERO) ? '0' : ' ';
813 /* only one fix required per string */
814 break;
818 #endif /* WIN32 */
820 targ = Parrot_str_append(interp, targ, cstr2pstr(tc));
822 break;
824 /* STRINGS */
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) {
830 PMC * const tmp =
831 VTABLE_get_pmc_keyed_int(interp,
832 ((PMC *)obj->data),
833 (obj->index));
835 STRING * const string = (VTABLE_get_repr(interp, tmp));
836 STRING * const ts = handle_flags(interp, &info,
837 string, 0, NULL);
838 obj->index++;
840 targ = Parrot_str_append(interp, targ, ts);
841 break;
844 case 's':
845 CASE_s:
847 STRING * const string = obj->getstring(interp,
848 info.type, obj);
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);
856 break;
858 default:
859 /* fake the old %P and %S commands */
860 if (info.type == SIZE_PMC
861 || info.type == SIZE_PSTR) {
862 i--;
863 goto CASE_s;
864 /* case 's' will see the SIZE_PMC or SIZE_PSTR
865 * and assume it was %Ps (or %Ss). Genius,
866 * no? */
868 else {
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;
876 break;
878 case PHASE_DONE:
879 default:
880 /* This is the terminating condition of the surrounding
881 * loop, so...
883 PANIC(interp, "We can't be here");
888 old = i;
889 i--;
891 else {
892 len++;
895 if (len) {
896 STRING *ignored = Parrot_str_substr(interp, pat, old, len, &substr, 1);
897 UNUSED(ignored);
898 targ = Parrot_str_append(interp, targ, substr);
901 return targ;
906 =back
908 =head1 SEE ALSO
910 F<src/misc.h>, F<src/misc.c>, F<src/spf_vtable.c>.
912 =cut
918 * Local variables:
919 * c-file-style: "parrot"
920 * End:
921 * vim: expandtab shiftwidth=4: