Release 2.5.0
[parrot.git] / src / spf_render.c
blob97feabe5bf7627fb1524d1de5b2d2a0be8129173
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 "spf_render.str"
27 typedef enum {
28 PHASE_FLAGS = 0,
29 PHASE_WIDTH,
30 PHASE_PREC,
31 PHASE_TYPE,
32 PHASE_TERM,
33 PHASE_DONE
34 } PHASE;
36 typedef struct SpfInfo_tag {
37 UINTVAL width;
38 UINTVAL prec;
39 INTVAL flags;
40 INTVAL type;
41 PHASE phase;
42 } SpfInfo;
44 enum {
45 FLAG_MINUS = (1<<0),
46 FLAG_PLUS = (1<<1),
47 FLAG_ZERO = (1<<2),
48 FLAG_SPACE = (1<<3),
49 FLAG_SHARP = (1<<4),
50 FLAG_WIDTH = (1<<5),
51 FLAG_PREC = (1<<6)
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(
60 ARGOUT(char *out),
61 ARGMOD(SpfInfo *info),
62 int thingy)
63 __attribute__nonnull__(1)
64 __attribute__nonnull__(2)
65 FUNC_MODIFIES(*out)
66 FUNC_MODIFIES(*info);
68 PARROT_WARN_UNUSED_RESULT
69 PARROT_CANNOT_RETURN_NULL
70 static STRING * handle_flags(PARROT_INTERP,
71 ARGIN(const SpfInfo *info),
72 ARGIN(STRING *str),
73 INTVAL is_int_type,
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,
81 ARGOUT(STRING *dest),
82 ARGIN(const SpfInfo *info),
83 ARGMOD(STRING *src),
84 ARGIN_NULLOK(STRING *prefix))
85 __attribute__nonnull__(1)
86 __attribute__nonnull__(2)
87 __attribute__nonnull__(3)
88 __attribute__nonnull__(4)
89 FUNC_MODIFIES(*dest)
90 FUNC_MODIFIES(*src);
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
109 * around for us.
111 #ifdef _MSC_VER
112 # define snprintf _snprintf
113 #endif
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.
122 =cut
126 PARROT_WARN_UNUSED_RESULT
127 PARROT_CANNOT_RETURN_NULL
128 static STRING *
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);
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 str = Parrot_str_chopn(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);
147 ++len;
149 else if (info->flags & FLAG_SPACE) {
150 STRING * const cs = CONST_STRING(interp, " ");
151 str = Parrot_str_concat(interp, cs, str);
152 ++len;
156 /* # 0x ... */
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 ??? */
163 else {
164 /* string precision */
165 if (info->flags & FLAG_PREC && info->prec == 0) {
166 str = Parrot_str_chopn(interp, str, len);
167 len = 0;
169 else if (info->flags & FLAG_PREC && info->prec < len) {
170 str = Parrot_str_chopn(interp, str, -(INTVAL)(info->prec));
171 len = 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) == '+')) {
190 STRING *temp = NULL;
191 STRING *ignored;
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);
197 else {
198 str = Parrot_str_concat(interp, fill, str);
202 return 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.
215 =cut
219 PARROT_CANNOT_RETURN_NULL
220 static STRING*
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);
227 return dest;
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
236 a float.
238 =cut
242 static void
243 gen_sprintf_call(ARGOUT(char *out), ARGMOD(SpfInfo *info), int thingy)
245 ASSERT_ARGS(gen_sprintf_call)
247 const int flags = info->flags;
248 char *p = out;
249 *p++ = '%';
251 if (flags) {
252 if (flags & FLAG_MINUS)
253 *p++ = '-';
255 if (flags & FLAG_PLUS)
256 *p++ = '+';
258 if (flags & FLAG_ZERO)
259 *p++ = '0';
261 if (flags & FLAG_SPACE)
262 *p++ = ' ';
264 if (flags & FLAG_SHARP)
265 *p++ = '#';
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;
278 *p++ = '.';
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 */
287 *p++ = 'l';
288 *p++ = 'l';
291 *p++ = (char)thingy;
292 *p = '\0';
298 =item C<STRING * Parrot_sprintf_format(PARROT_INTERP, const STRING *pat,
299 SPRINTF_OBJ *obj)>
301 This is the engine that does all the formatting.
303 =cut
307 PARROT_WARN_UNUSED_RESULT
308 PARROT_CANNOT_RETURN_NULL
309 STRING *
310 Parrot_sprintf_format(PARROT_INTERP, ARGIN(const STRING *pat), ARGMOD(SPRINTF_OBJ *obj))
312 ASSERT_ARGS(Parrot_sprintf_format)
313 INTVAL i;
314 INTVAL len = 0;
315 INTVAL old = 0;
316 const INTVAL pat_len = (INTVAL)Parrot_str_byte_length(interp, pat);
317 HUGEINTVAL num;
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) == '%') { /* % */
331 if (len) {
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);
336 len = 0;
337 old = i;
338 if (string_ord(interp, pat, i + 1) == '%') {
339 /* skip this one, make next the first char
340 * of literal sequence, starting at old */
341 ++i;
342 ++old;
343 ++len;
344 continue;
346 else {
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 */
355 tc[0] = '\0';
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 {
361 * rule format {
362 * <other_stuff> (<field> <other_stuff>)*
365 * rule other_stuff {
366 * [<[^\%]> | \%\%]*:
369 * rule field {
370 * \%
371 * <flags>?
372 * <width>?
373 * [\.<prec>]?
374 * <size>?
375 * <term>
378 * rule flags {
379 * <[
380 * + # prefix with a + if necessary
381 * - # left-align
382 * 0 # zero-pad
383 * <sp> # space-pad
384 * \# # 0, 0x on octal, hex; force decimal point on float
385 * ]>+
388 * rule width {
389 * [\d|\*]+ # minimum width
392 * rule prec {
393 * [\d|\*]+ # width on integers;
394 * # number of digits after decimal on floats;
395 * # maximum width on strings
398 * rule size {
399 * <[
400 * h # short (or float)
401 * l # long
402 * H # HUGEwhateverVAL (long [long]?, [long]? double)
403 * v # whateverVAL
404 * O # opcode_t
405 * P # really a PMC
406 * S # Parrot string (only with %s)
407 * ]>
410 * rule term {
411 * <[
412 * c # char
413 * d # integer
414 * i # integer
415 * o # octal
416 * x # hex
417 * X # hex with capital X (if #)
418 * b # binary
419 * B # binary with capital B (if #)
420 * u # unsigned integer
421 * p # pointer
423 * e # 1e1
424 * E # 1E1
425 * f # 1.0
426 * g # 1, 0.1, 1e1
427 * G # 1, 0.1, 1E1
429 * s # string
430 * ]>
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
437 * supported.
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:
453 switch (ch) {
454 case '-':
455 info.flags |= FLAG_MINUS;
456 continue;
458 case '+':
459 info.flags |= FLAG_PLUS;
460 continue;
462 case '0':
463 info.flags |= FLAG_ZERO;
464 continue;
466 case ' ':
467 info.flags |= FLAG_SPACE;
468 continue;
470 case '#':
471 info.flags |= FLAG_SHARP;
472 continue;
474 default:
475 info.phase = PHASE_WIDTH;
479 /*@fallthrough@ */ case PHASE_WIDTH:
480 switch (ch) {
481 case '0':
482 case '1':
483 case '2':
484 case '3':
485 case '4':
486 case '5':
487 case '6':
488 case '7':
489 case '8':
490 case '9':
491 info.flags |= FLAG_WIDTH;
492 info.width *= 10;
493 info.width += ch - '0';
494 continue;
496 case '*':
497 info.flags |= FLAG_WIDTH;
498 num = obj->getint(interp, SIZE_XVAL, obj);
499 if (num < 0) {
500 info.flags |= FLAG_MINUS;
501 info.width = -num;
503 else {
504 info.width = num;
506 continue;
508 case '.':
509 info.phase = PHASE_PREC;
510 continue;
512 default:
513 info.phase = PHASE_PREC;
517 /*@fallthrough@ */ case PHASE_PREC:
518 switch (ch) {
519 case '0':
520 case '1':
521 case '2':
522 case '3':
523 case '4':
524 case '5':
525 case '6':
526 case '7':
527 case '8':
528 case '9':
529 info.flags |= FLAG_PREC;
530 info.prec *= 10;
531 info.prec += ch - '0';
532 continue;
534 case '*':
535 info.flags |= FLAG_PREC;
536 info.prec = (UINTVAL)obj->getint(interp,
537 SIZE_XVAL, obj);
538 info.phase = PHASE_TYPE;
539 continue;
541 default:
542 info.phase = PHASE_TYPE;
545 /*@fallthrough@ */ case PHASE_TYPE:
546 switch (ch) {
547 case 'h':
548 info.type = SIZE_SHORT;
549 continue;
551 case 'l':
552 info.type = SIZE_LONG;
553 continue;
555 case 'L':
556 case 'H':
557 info.type = SIZE_HUGE;
558 continue;
560 case 'v':
561 info.type = SIZE_XVAL;
562 continue;
564 case 'O':
565 info.type = SIZE_OPCODE;
566 continue;
568 case 'P':
569 info.type = SIZE_PMC;
570 continue;
572 case 'S':
573 info.type = SIZE_PSTR;
574 continue;
576 default:
577 info.phase = PHASE_TERM;
581 /*@fallthrough@ */ case PHASE_TERM:
582 switch (ch) {
583 /* INTEGERS */
584 case 'c':
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);
590 break;
592 case 'o':
594 const UHUGEINTVAL theuint =
595 obj->getuint(interp, info.type, obj);
596 STRING * const ts =
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,
603 &info, ts, prefix);
605 break;
607 case 'x':
609 const UHUGEINTVAL theuint =
610 obj->getuint(interp, info.type, obj);
611 STRING * const ts =
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,
618 &info, ts, prefix);
620 break;
622 case 'X':
624 STRING * const prefix = CONST_STRING(interp, "0X");
625 const UHUGEINTVAL theuint =
626 obj->getuint(interp, info.type, obj);
627 STRING * ts =
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,
634 &info, ts, prefix);
636 break;
638 case 'b':
640 STRING * const prefix = CONST_STRING(interp, "0b");
641 const UHUGEINTVAL theuint =
642 obj->getuint(interp, info.type, obj);
643 STRING * const ts =
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,
649 &info, ts, prefix);
651 break;
653 case 'B':
655 STRING * const prefix = CONST_STRING(interp, "0B");
656 const HUGEINTVAL theint =
657 obj->getint(interp, info.type, obj);
658 STRING * const ts =
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,
664 &info, ts, prefix);
666 break;
668 case 'u':
670 const UHUGEINTVAL theuint =
671 obj->getuint(interp, info.type, obj);
672 sharedint = theuint;
674 goto do_sprintf;
675 case 'd':
676 case 'i':
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);
684 do_sprintf:
686 STRING *ts;
687 gen_sprintf_call(tc, &info, ch);
688 ts = cstr2pstr(tc);
690 char * const tempstr =
691 Parrot_str_to_cstring(interp, ts);
693 #ifdef PARROT_HAS_SNPRINTF
694 snprintf(tc, PARROT_SPRINTF_BUFFER_SIZE,
695 tempstr, sharedint);
696 #else
697 /* the buffer is 4096, so no problem here */
698 sprintf(tc, tempstr, sharedint);
699 #endif
700 Parrot_str_free_cstring(tempstr);
702 targ = Parrot_str_concat(interp, targ, cstr2pstr(tc));
704 break;
706 case 'p':
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,
715 ts, prefix);
717 break;
719 /* FLOATS - We cheat on these and use snprintf. */
720 case 'e':
721 case 'E':
722 case 'f':
723 case 'g':
724 case 'G':
726 STRING *ts;
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);
740 else {
741 /* turn -0.0 into 0.0 */
742 gen_sprintf_call(tc, &info, ch);
743 ts = cstr2pstr(tc);
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,
753 tempstr,
754 (double)thefloat);
755 #else
756 /* the buffer is 4096, so no problem here */
757 sprintf(tc, tempstr, (double)thefloat);
758 #endif
759 Parrot_str_free_cstring(tempstr);
762 #ifdef WIN32
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);
771 size_t j;
772 for (j = 0; j < tclen; ++j) {
773 if ((tc[j] == 'e' || tc[j] == 'E')
774 && (tc[j+1] == '+' || tc[j+1] == '-')
775 && tc[j+2] == '0'
776 && isdigit((unsigned char)tc[j+3])
777 && isdigit((unsigned char)tc[j+4]))
779 mem_sys_memmove(&tc[j+2], &tc[j+3],
780 strlen(&tc[j+2]));
782 /* now fix any broken length */
784 if ((info.flags & FLAG_WIDTH)
785 && strlen(tc) < info.width) {
786 if (info.flags & FLAG_MINUS)
787 strcat(tc, " ");
788 else {
789 mem_sys_memmove(&tc[1], &tc[0],
790 strlen(tc) + 1);
791 tc[0] = (info.flags & FLAG_ZERO) ? '0' : ' ';
795 /* only one fix required per string */
796 break;
800 #endif /* WIN32 */
802 targ = Parrot_str_concat(interp, targ, cstr2pstr(tc));
804 break;
806 /* STRINGS */
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) {
812 PMC * const tmp =
813 VTABLE_get_pmc_keyed_int(interp,
814 ((PMC *)obj->data),
815 (obj->index));
817 STRING * const string = (VTABLE_get_repr(interp, tmp));
818 STRING * const ts = handle_flags(interp, &info,
819 string, 0, NULL);
820 ++obj->index;
822 targ = Parrot_str_concat(interp, targ, ts);
823 break;
826 case 's':
827 CASE_s:
829 STRING * const string = obj->getstring(interp,
830 info.type, obj);
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);
838 break;
840 default:
841 /* fake the old %P and %S commands */
842 if (info.type == SIZE_PMC
843 || info.type == SIZE_PSTR) {
844 --i;
845 goto CASE_s;
846 /* case 's' will see the SIZE_PMC or SIZE_PSTR
847 * and assume it was %Ps (or %Ss). Genius,
848 * no? */
850 else {
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;
858 break;
860 case PHASE_DONE:
861 default:
862 /* This is the terminating condition of the surrounding
863 * loop, so...
865 PANIC(interp, "We can't be here");
870 old = i;
871 --i;
873 else {
874 ++len;
877 if (len) {
878 substr = Parrot_str_substr(interp, pat, old, len);
879 targ = Parrot_str_concat(interp, targ, substr);
882 return targ;
887 =back
889 =head1 SEE ALSO
891 F<src/misc.h>, F<src/misc.c>, F<src/spf_vtable.c>.
893 =cut
899 * Local variables:
900 * c-file-style: "parrot"
901 * End:
902 * vim: expandtab shiftwidth=4: