pike.git / src / string_builder.c

version» Context lines:

pike.git/src/string_builder.c:1: + /* + || 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 "stuff.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) + { +  unsigned INT64 tmp; +  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) + { +  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); +  } +  } + } +  +  + 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); + }   Newline at end of file added.