2a3bfa2017-01-26Henrik Grubbström (Grubba) /* || This file is part of Pike. For copyright information see COPYRIGHT. || Pike is distributed under GPL, LGPL and MPL. See the file COPYING || for more information. */ #include "global.h" #include "stralloc.h" #include "string_builder.h" #include "pike_macros.h" #include "buffer.h" #include "pike_macros.h" #include "pike_memory.h" #include "pike_error.h" #include "gc.h" #include "bignum.h" #include "interpret.h" #include "operators.h" #include "pike_float.h" #include "pike_types.h" #include "block_allocator.h" #include "whitespace.h" #include <errno.h> PMOD_EXPORT void init_string_builder_alloc(struct string_builder *s, ptrdiff_t length, int mag) { s->s=begin_wide_shared_string(length,mag); s->malloced=length; s->known_shift=0; s->s->len=0; low_set_index (s->s, 0, 0); } PMOD_EXPORT void init_string_builder(struct string_builder *s, int mag) { init_string_builder_alloc(s, 256, mag); } PMOD_EXPORT void init_string_builder_copy(struct string_builder *to, const struct string_builder *from) { to->malloced = from->malloced; to->s = begin_wide_shared_string (from->malloced, from->s->size_shift); to->s->len = from->s->len; memcpy (to->s->str, from->s->str, (from->s->len + 1) << from->s->size_shift); to->known_shift = from->known_shift; } /* str becomes invalid if successful (i.e. nonzero returned), * otherwise nothing happens. */ PMOD_EXPORT int init_string_builder_with_string (struct string_builder *s, struct pike_string *str) { if (string_may_modify(str)) { /* Unlink the string and use it as buffer directly. */ unlink_pike_string (str); str->flags = STRING_NOT_SHARED; s->s = str; s->malloced = str->len; s->known_shift = str->size_shift; return 1; } return 0; } PMOD_EXPORT void string_build_mkspace(struct string_builder *s, ptrdiff_t chars, int mag) /* Doesn't touch or sanity check s->known_shift. */ { if(mag > s->s->size_shift) { struct pike_string *n; ptrdiff_t l = s->s->len + chars; if (l < s->malloced) l = s->malloced; n=begin_wide_shared_string(l,mag); pike_string_cpy(MKPCHARP_STR(n),s->s); n->len=s->s->len; s->s->len = s->malloced; /* Restore the real length */ s->malloced=l; free_string(s->s); s->s=n; } else if(s->s->len+chars > s->malloced) { ptrdiff_t newlen = MAXIMUM(s->malloced*2, s->s->len + chars); ptrdiff_t oldlen = s->s->len; s->s->len = s->malloced; /* Restore the real length */ s->s = realloc_unlinked_string(s->s, newlen); s->s->len = oldlen; s->malloced = newlen; } } PMOD_EXPORT void *string_builder_allocate(struct string_builder *s, ptrdiff_t chars, int mag) { void *ret; string_build_mkspace(s, chars, mag); if(chars<0) s->known_shift=0; ret = s->s->str + (s->s->len<<s->s->size_shift); s->s->len += chars; return ret; } PMOD_EXPORT void string_builder_putchar(struct string_builder *s, int ch) { ptrdiff_t i; enum size_shift mag = min_magnitude(ch); string_build_mkspace(s, 1, mag); if (mag > s->known_shift) { s->known_shift = mag; } i = s->s->len++; low_set_index(s->s,i,ch); } PMOD_EXPORT void string_builder_putchars(struct string_builder *s, int ch, ptrdiff_t count) { ptrdiff_t len = s->s->len; enum size_shift mag = min_magnitude(ch); /* This is not really expected to happen. But since we are doing * memset here, a negative argument should be avoided. */ if (count < 0) Pike_fatal("Non-positive count in call to string_builder_putchars().\n"); if (!count) return; string_build_mkspace(s, count, mag); if (mag > s->known_shift) { s->known_shift = mag; } switch (s->s->size_shift) { case 0: memset (STR0 (s->s) + s->s->len, ch, count); break; case 1: { int i; for (i = 0; i < count; i++) (STR1 (s->s) + s->s->len)[i] = ch; break; } case 2: { int i; for (i = 0; i < count; i++) (STR2 (s->s) + s->s->len)[i] = ch; break; } } s->s->len += count; } PMOD_EXPORT void string_builder_binary_strcat0(struct string_builder *s, const p_wchar0 *str, ptrdiff_t len) { string_build_mkspace(s,len,0); switch(s->s->size_shift) { case 0: convert_0_to_0(STR0(s->s)+s->s->len,str,len); break; case 1: convert_0_to_1(STR1(s->s)+s->s->len,str,len); break; case 2: convert_0_to_2(STR2(s->s)+s->s->len,str,len); break; } s->s->len+=len; } PMOD_EXPORT void string_builder_binary_strcat1(struct string_builder *s, const p_wchar1 *str, ptrdiff_t len) { if (s->s->size_shift == 0) { if (find_magnitude1 (str, len) == 0) { string_build_mkspace (s, len, 0); convert_1_to_0 (STR0(s->s) + s->s->len, str, len); s->s->len += len; return; } s->known_shift = 1; } string_build_mkspace (s, len, 1); if (s->s->size_shift == 1) convert_1_to_1 (STR1(s->s)+s->s->len, str, len); else { #ifdef PIKE_DEBUG if (s->s->size_shift != 2) Pike_fatal ("I aint got no clue 'bout nothing, dude. (%d)\n", s->s->size_shift); #endif convert_1_to_2 (STR2(s->s)+s->s->len, str, len); } s->s->len += len; } PMOD_EXPORT void string_builder_binary_strcat2(struct string_builder *s, const p_wchar2 *str, ptrdiff_t len) { if (s->s->size_shift < 2) { enum size_shift shift = find_magnitude2 (str, len); if (shift > s->s->size_shift) { string_build_mkspace (s, len, shift); if (shift == 1) convert_2_to_1 (STR1(s->s) + s->s->len, str, len); else { #ifdef PIKE_DEBUG if (shift != 2) Pike_fatal ("Uhh.. Like, what? (%d)\n", shift); #endif convert_2_to_2 (STR2(s->s) + s->s->len, str, len); } s->known_shift = shift; } else { string_build_mkspace (s, len, 0); if (s->s->size_shift == 0) convert_2_to_0 (STR0(s->s) + s->s->len, str, len); else { #ifdef PIKE_DEBUG if (s->s->size_shift != 1) Pike_fatal ("This is soo way bogus, man. (%d)\n", s->s->size_shift); #endif convert_2_to_1 (STR1(s->s) + s->s->len, str, len); } } } else { string_build_mkspace (s, len, 2); convert_2_to_2 (STR2(s->s) + s->s->len, str, len); } s->s->len += len; } PMOD_EXPORT void string_builder_append(struct string_builder *s, const PCHARP from, ptrdiff_t len) { enum size_shift shift = from.shift; if (shift > s->s->size_shift) { if (shift == 1) { shift = find_magnitude1((p_wchar1 *)from.ptr, len); } else { shift = find_magnitude2((p_wchar2 *)from.ptr, len); } if (shift > s->known_shift) s->known_shift = shift; } string_build_mkspace(s, len, shift); generic_memcpy(MKPCHARP_STR_OFF(s->s,s->s->len), from, len); s->s->len+=len; } PMOD_EXPORT void string_builder_fill(struct string_builder *s, ptrdiff_t howmany, const PCHARP from, ptrdiff_t len, ptrdiff_t offset) { ptrdiff_t tmp; enum size_shift shift; #ifdef PIKE_DEBUG if(len<=0) Pike_fatal("Cannot fill with zero length strings!\n"); #endif if(howmany<=0) return; if(!s->s->size_shift && len == 1 && (!from.shift || !min_magnitude(EXTRACT_PCHARP(from)))) { memset(string_builder_allocate(s,howmany,0), EXTRACT_PCHARP(from), howmany); return; } if ((shift = from.shift) > s->s->size_shift) { /* Check if we really need the extra magnitude. */ /* FIXME: What about offset? */ if (shift == 1) { shift = find_magnitude1((p_wchar1 *)from.ptr, len); } else { shift = find_magnitude2((p_wchar2 *)from.ptr, len); } } string_build_mkspace(s, howmany, shift); tmp = MINIMUM(howmany, len - offset); generic_memcpy(MKPCHARP_STR_OFF(s->s,s->s->len), ADD_PCHARP(from,offset), tmp); s->s->len+=tmp; howmany-=tmp; if(howmany > 0) { PCHARP to; tmp=MINIMUM(howmany, len); to=MKPCHARP_STR_OFF(s->s,s->s->len); generic_memcpy(to,from, tmp); s->s->len+=tmp; howmany-=tmp; while(howmany > 0) { tmp = MINIMUM(len, howmany); memcpy(s->s->str + (s->s->len << s->s->size_shift), to.ptr, tmp << s->s->size_shift); len+=tmp; howmany-=tmp; s->s->len+=tmp; } } } /* Append a NUL-terminated UTF16 string possibly containing surrogates. */ PMOD_EXPORT void string_builder_utf16_strcat(struct string_builder *s, const p_wchar1 *utf16str) { p_wchar1 uc; while ((uc = *(utf16str++))) { if ((uc & 0xf800) == 0xd800) { /* Surrogate. */ p_wchar2 wchar = uc & 0x03ff; if (!(uc & 0x0400)) { /* High order 10 bits. */ wchar <<= 10; } uc = *(utf16str++); if (uc & 0x0400) { /* Low order 10 bits. */ wchar |= (uc & 0x3ff); } else { /* High order 10 bits. */ wchar |= (uc & 0x3ff) << 10; } string_builder_putchar(s, wchar + 0x00010000); } else { string_builder_putchar(s, uc); } } } PMOD_EXPORT void string_builder_strcat(struct string_builder *s, const char *str) { string_builder_binary_strcat(s,str,strlen(str)); } PMOD_EXPORT void string_builder_shared_strcat(struct string_builder *s, const struct pike_string *str) { string_build_mkspace(s,str->len,str->size_shift); pike_string_cpy(MKPCHARP_STR_OFF(s->s,s->s->len), str); s->known_shift=MAXIMUM(s->known_shift,str->size_shift); s->s->len+=str->len; } PMOD_EXPORT ptrdiff_t string_builder_quote_string(struct string_builder *buf, const struct pike_string *str, ptrdiff_t i, ptrdiff_t max_len, int flags) { ptrdiff_t old_len = buf->s->len; int inhibit_whitespace = (flags & QUOTE_NORMALIZE_WS); for (; i < str->len; i++) { p_wchar2 ch = index_shared_string(str, i); if (ch < 0 || ch > 0xffff) { /* Huge character. */ string_builder_binary_strcat(buf, "\\U", 2); string_builder_append_integer(buf, (unsigned INT32)ch, 16, APPEND_ZERO_PAD, 8, 8); inhibit_whitespace = 0; } else if (ch > 0xff) { /* Unicode character. */ string_builder_binary_strcat(buf, "\\u", 2); string_builder_append_integer(buf, ch, 16, APPEND_ZERO_PAD, 4, 4); inhibit_whitespace = 0; } else if (ch & 0x60) { /* Printable character or DEL. */ if (ch == '\177' || ch == ' ') { /* DEL or SP */ goto ctrl_char; } inhibit_whitespace = 0; if ((ch == '"') || (ch == '\\')) { string_builder_putchar(buf, '\\'); } string_builder_putchar(buf, ch); } else { p_wchar2 next_ch; ctrl_char: /* Control character or whitespace. */ if (flags & QUOTE_NORMALIZE_WS) { /* Normalize all sequences of whitespace to a single SP. */ if (!inhibit_whitespace) { string_builder_putchar(buf, ' '); inhibit_whitespace = 1; } goto next; } string_builder_putchar(buf, '\\'); if ((ch > 6) && (ch < 14)) { string_builder_putchar(buf, "0123456abtnvfr"[ch]); if ((ch == 10) && (flags & QUOTE_BREAK_AT_LF)) { if (buf->s->len > max_len) { /* Too bad; no place for the lf. */ buf->s->len = old_len; return i; } return i+1; } goto next; } if (ch == 27) { string_builder_putchar(buf, 'e'); goto next; } /* Check if we can use an octal escape. */ if ((i+1 < str->len) && ((next_ch = index_shared_string(str, i+1)) >= '0') && (next_ch <= '7')) { /* No. */ if (flags & QUOTE_NO_STRING_CONCAT) { string_builder_putchar(buf, 'u'); string_builder_append_integer(buf, ch, 16, APPEND_ZERO_PAD, 4, 4); } else { string_builder_append_integer(buf, ch, 8, 0, 1, 1); string_builder_binary_strcat(buf, "\"\"", 2); } goto next; } string_builder_append_integer(buf, ch, 8, 0, 1, 1); } next: if (buf->s->len > max_len) { buf->s->len = old_len; inhibit_whitespace = 0; break; } old_len = buf->s->len; } if (inhibit_whitespace && (inhibit_whitespace != QUOTE_NORMALIZE_WS)) { /* Get rid of the SP at the end of the buffer. */ buf->s->len--; } return i; } PMOD_EXPORT void string_builder_append_integer(struct string_builder *s, INT64 val, unsigned int base, int flags, size_t min_width, size_t precision) {
d559142017-07-17Martin Nilsson  UINT64 tmp;
2a3bfa2017-01-26Henrik Grubbström (Grubba)  size_t len = 1; const char *numbers = "0123456789abcdef"; if ((base < 2) || (base > 16)) { Pike_fatal("string_builder_append_int(): Unsupported base %u.\n", base); } if (flags & APPEND_UPPER_CASE) { numbers = "0123456789ABCDEF"; } if ((flags & APPEND_SIGNED) && (val < 0)) { string_builder_putchar(s, '-'); val = -val; } else if (flags & APPEND_POSITIVE) { string_builder_putchar(s, '+'); } if ((flags & APPEND_ZERO_PAD) && (precision < min_width)) { precision = min_width; } tmp = val; if (base & (base - 1)) { size_t cnt; /* Calculate the output length. * Use do-while to ensure that zero isn't output as an empty string. */ len = 0; do { len++; tmp /= base; } while (tmp); /* Precision is minimum number of digits. */ if (len < precision) len = precision; /* Perform padding. */ if (!(flags & APPEND_LEFT)) { if (len < min_width) { string_builder_fill(s, min_width - len, MKPCHARP(" ", 0), 4, 0); } min_width = 0; } cnt = len; tmp = val; switch(s->s->size_shift) { case 0: { p_wchar0 *p = string_builder_allocate(s, len, 0); do { p[--cnt] = numbers[tmp%base]; tmp /= base; } while (cnt); } break; case 1: { p_wchar1 *p = string_builder_allocate(s, len, 0); do { p[--cnt] = numbers[tmp%base]; tmp /= base; } while (cnt); } break; case 2: { p_wchar2 *p = string_builder_allocate(s, len, 0); do { p[--cnt] = numbers[tmp%base]; tmp /= base; } while (cnt); } break; } } else { /* base is a power of two, so we can do without * the division and modulo operations. */ int delta; size_t shift; unsigned int mask; for(delta = 1; (base>>delta) > 1; delta++) ; mask = (1<<delta)-1; /* Usually base-1. */ /* Precision is minimum number of digits. */ if (precision) shift = (len = precision) * delta; else shift = delta; /* Calculate actual number of digits and initial shift. */ for (; shift < SIZEOF_INT64 * 8 && tmp >> shift; shift += delta, len++) ; if ((len < min_width) && !(flags & APPEND_LEFT)) { /* Perform padding. * Note that APPEND_ZERO_PAD can not be active here, since * len is at least min_width in that case. */ string_builder_fill(s, min_width - len, MKPCHARP(" ", 0), 4, 0); min_width = 0; } while(shift) { shift -= delta; string_builder_putchar(s, numbers[(tmp>>shift) & mask]); } } if (len < min_width) { /* Perform padding. * Note that APPEND_ZERO_PAD can not be active here, since * len is at least min_width in that case. * Note that APPEND_LEFT is always active here, since * min_width isn't zero. */ string_builder_fill(s, min_width - len, MKPCHARP(" ", 0), 4, 0); } } /* Kludge around brokeness of gcc/x86_64 */ #ifdef VA_LIST_IS_STATE_PTR #define VA_LIST_PTR va_list #define VA_LIST_ADDR(X) (X) #define VA_LIST_DEREF(X) (X) #else #define VA_LIST_PTR va_list * #define VA_LIST_ADDR(X) (&(X)) #define VA_LIST_DEREF(X) (*(X)) #endif static INT64 pike_va_int(VA_LIST_PTR args, int flags) { switch (flags & (APPEND_WIDTH_MASK|APPEND_SIGNED)) { case APPEND_WIDTH_HALF: return va_arg(VA_LIST_DEREF(args), unsigned int) & 0xffff; case APPEND_WIDTH_HALF|APPEND_SIGNED: return (short)va_arg(VA_LIST_DEREF(args), int); case 0: return va_arg(VA_LIST_DEREF(args), unsigned int); case APPEND_SIGNED: return va_arg(VA_LIST_DEREF(args), int); case APPEND_WIDTH_LONG: return va_arg(VA_LIST_DEREF(args), unsigned long); case APPEND_WIDTH_LONG|APPEND_SIGNED: return va_arg(VA_LIST_DEREF(args), long); case APPEND_WIDTH_LONG_LONG: return va_arg(VA_LIST_DEREF(args), UINT64); case APPEND_WIDTH_LONG_LONG|APPEND_SIGNED: return va_arg(VA_LIST_DEREF(args), INT64); } Pike_fatal("string_builder_append_integerv(): Unsupported flags: 0x%04x\n", flags); UNREACHABLE(return 0); } /* Standard formats supported by string_builder_{v,}sprintf(): * * '%' Insert %. * 'a' Insert double. * 'c' Insert character. * 'd' Insert decimal integer. * 'e' Insert double. * 'f' Insert double. * 'g' Insert double. * 'o' Insert octal integer. * 's' Insert string. * 'u' Insert unsigned decimal integer. * 'x' Insert lower-case hexadecimal integer. * 'E' Insert double. * 'G' Insert double. * 'X' Insert upper-case hexadecimal integer. * * Format modifiers supported by string_builder_{v,}sprintf(): * * '+' Explicit sign for non-negative numeric values. * '-' Align left. * '0' Zero pad. * '0'..'9' Field width. * '.' Precision field width follows. * 'h' Half-width input. * 'l' Long(-er) input. * 't' Pointer-width input (ptrdiff_t). * 'w' Wide input (same as 'l', but only for strings). * 'z' Pointer-width input (size_t). * * Extended formats supported by string_builder_{v,}sprintf(): * * 'b' Insert binary integer. * 'O' Insert description of svalue. * 'S' Insert pike_string. * 'T' Insert pike_type. */ /* Values used internally in string_builder_vsprintf() */ #define STATE_MIN_WIDTH 1 #define STATE_PRECISION 2 PMOD_EXPORT void string_builder_vsprintf(struct string_builder *s, const char *fmt, va_list args) {
716caf2018-01-03Henrik Grubbström (Grubba)  STACK_LEVEL_START(0);
2a3bfa2017-01-26Henrik Grubbström (Grubba)  while (*fmt) { if (*fmt == '%') { int flags = 0; size_t min_width = 0; size_t precision = 0; int state = 0; fmt++; while (1) { switch (*(fmt++)) { case '%': string_builder_putchar(s, '%'); break; case '+': flags |= APPEND_POSITIVE; continue; case '-': flags |= APPEND_LEFT; continue; case '0': if (!state) { flags |= APPEND_ZERO_PAD; } /* FALL_THROUGH */ case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': if (state == STATE_PRECISION) { precision = precision * 10 + fmt[-1] - '0'; } else { state = STATE_MIN_WIDTH; min_width = min_width * 10 + fmt[-1] - '0'; } continue; case '*': if (state == STATE_PRECISION) { precision = va_arg(args, int); } else { state = STATE_MIN_WIDTH; min_width = va_arg(args, int); } continue; case '.': state = STATE_PRECISION; continue; case 'h': flags |= APPEND_WIDTH_HALF; continue; case 'w': /* Same as l, but old-style, and only for %s. */ case 'l': if (flags & APPEND_WIDTH_LONG) { flags |= APPEND_WIDTH_LONG_LONG; } else { flags |= APPEND_WIDTH_LONG; } continue; case 't': /* ptrdiff_t */ case 'z': /* size_t */ flags = (flags & ~APPEND_WIDTH_MASK) | APPEND_WIDTH_PTR; continue; case 'T': /* struct pike_type */ /* FIXME: Doesn't care about field or integer widths yet. */ low_describe_type(s, va_arg(args, struct pike_type *)); break; case 'O': { /* FIXME: Doesn't care about field or integer widths yet. */ struct byte_buffer buf = BUFFER_INIT(); describe_svalue(&buf, va_arg(args, struct svalue *), 0, NULL); string_builder_binary_strcat(s, buffer_ptr(&buf), buffer_content_length(&buf)); buffer_free(&buf); } break; case 'S': /* Note: On some platforms this is an alias for %ls, so if you * want to output wchar_t strings, use %ls instead! */ { struct pike_string *str = va_arg(args, struct pike_string *); size_t len = str->len; if (precision && (precision < len)) len = precision; if (min_width > len) { if (flags & APPEND_LEFT) { string_builder_append(s, MKPCHARP_STR(str), len); string_builder_fill(s, min_width - len, MKPCHARP(" ", 0), 4, 0); } else { string_builder_fill(s, min_width - len, MKPCHARP(" ", 0), 4, 0); string_builder_append(s, MKPCHARP_STR(str), len); } } else { string_builder_append(s, MKPCHARP_STR(str), len); } } break; case 's': if (flags & APPEND_WIDTH_LONG) { /* Wide string: %ws, %ls or %lls */ PCHARP str; size_t len; if ((flags & APPEND_WIDTH_LONG)== APPEND_WIDTH_LONG) { str = MKPCHARP(va_arg(args, p_wchar1 *), 1); } else { str = MKPCHARP(va_arg(args, p_wchar2 *), 2); } len = pcharp_strlen(str); if (precision && precision < len) len = precision; if (min_width > len) { if (flags & APPEND_LEFT) { string_builder_append(s, str, len); string_builder_fill(s, min_width - len, MKPCHARP(" ", 0), 4, 0); } else { string_builder_fill(s, min_width - len, MKPCHARP(" ", 0), 4, 0); string_builder_append(s, str, len); } } else { string_builder_append(s, str, len); } } else { const char *str = va_arg(args, char *); size_t len = strlen(str); if (precision && precision < len) len = precision; if (min_width > len) { if (flags & APPEND_LEFT) { string_builder_binary_strcat(s, str, len); string_builder_fill(s, min_width - len, MKPCHARP(" ", 0), 4, 0); } else { string_builder_fill(s, min_width - len, MKPCHARP(" ", 0), 4, 0); string_builder_binary_strcat(s, str, len); } } else { string_builder_binary_strcat(s, str, len); } } break; case 'c': /* FIXME: Doesn't care about field or integer widths yet. */ string_builder_putchar(s, va_arg(args, int)); break; case 'b': string_builder_append_integer(s, pike_va_int(VA_LIST_ADDR(args), flags), 2, flags, min_width, precision); break; case 'o': string_builder_append_integer(s, pike_va_int(VA_LIST_ADDR(args), flags), 8, flags, min_width, precision); break; case 'x': string_builder_append_integer(s, pike_va_int(VA_LIST_ADDR(args), flags), 16, flags, min_width, precision); break; case 'X': string_builder_append_integer(s, pike_va_int(VA_LIST_ADDR(args), flags), 16, flags | APPEND_UPPER_CASE, min_width, precision); break; case 'u': string_builder_append_integer(s, pike_va_int(VA_LIST_ADDR(args), flags), 10, flags, min_width, precision); break; case 'd': flags |= APPEND_SIGNED; string_builder_append_integer(s, pike_va_int(VA_LIST_ADDR(args), flags), 10, flags, min_width, precision); break; /* %f used in modules/Image/colors.c. */ case 'a': case 'e': case 'E': case 'f': case 'g': case 'G': { double val = va_arg(args, double); size_t bytes; char nfmt[] = { '%', fmt[-1], 0 }; if (PIKE_ISNAN(val)) { /* NaN */ string_builder_strcat(s, "nan"); break; } if (val < 0.0) { string_builder_putchar(s, '-'); val = -val; } else if (flags & APPEND_POSITIVE) { string_builder_putchar(s, '+'); } if (PIKE_ISINF(val)) { /* Infinity */ string_builder_strcat(s, "inf"); break; } /* FIXME: Field lengths and precision. */ if ((bytes = snprintf(NULL, 0, nfmt, val))) { p_wchar0 *p = string_builder_allocate(s, bytes, 0); size_t check = snprintf((char*)p, bytes+1, nfmt, val); if (check != bytes) { Pike_fatal("string_builder_vsprintf(): snprintf(\"%s\", %f) " "is not trustworthy: " "%"PRINTSIZET"u != %"PRINTSIZET"u\n", nfmt, val, bytes, check); } if (s->s->size_shift) { /* We need to widen the string we just wrote. */ if (s->s->size_shift == 1) { p_wchar1 *p1 = (p_wchar1 *)p; while (bytes--) { p1[bytes] = p[bytes]; } } else { p_wchar2 *p2 = (p_wchar2 *)p; while (bytes--) { p2[bytes] = p[bytes]; } } } } } break; default: Pike_fatal("string_builder_vsprintf(): Invalid formatting method: " "\"%%%c\" 0x%x.\n", (fmt[-1] & 0xff), fmt[-1]); } break; } } else { const char *start = fmt; while (*fmt && (*fmt != '%')) fmt++; string_builder_binary_strcat(s, start, fmt-start); } }
716caf2018-01-03Henrik Grubbström (Grubba)  STACK_LEVEL_DONE(0);
2a3bfa2017-01-26Henrik Grubbström (Grubba) } PMOD_EXPORT void string_builder_sprintf(struct string_builder *s, const char *fmt, ...) { va_list args; va_start(args, fmt); string_builder_vsprintf(s, fmt, args); va_end(args); } PMOD_EXPORT void reset_string_builder(struct string_builder *s) { s->known_shift=0; s->s->len=0; } PMOD_EXPORT void free_string_builder(struct string_builder *s) { s->s->len = s->malloced; free_string(s->s); } PMOD_EXPORT struct pike_string *finish_string_builder(struct string_builder *s) { ptrdiff_t len = s->s->len; if (len != s->malloced) { s->s->len = s->malloced; /* Restore the allocated length. */ s->s = realloc_unlinked_string(s->s, len); } if(s->known_shift == s->s->size_shift) return low_end_shared_string(s->s); return end_shared_string(s->s); }