/* Generated from "/Users/hww3/devel/pike/src/string_builder.cmod" by precompile.pike running Pike v8.1 release 13
 *
 * Do NOT edit this file.
 */

#undef PRECOMPILE_API_VERSION
#define PRECOMPILE_API_VERSION 6



#undef cmod___CMOD__
#define cmod___CMOD__ 1
#line 1 "/Users/hww3/devel/pike/src/string_builder.cmod"
#line 1 "/Users/hww3/devel/pike/src/string_builder.cmod"
#line 1 "/Users/hww3/devel/pike/src/string_builder.cmod"
#line 3488 "/Users/hww3/devel/pike/lib/modules/Tools.pmod/Standalone.pmod/precompile.pike"







#line 3495 "/Users/hww3/devel/pike/lib/modules/Tools.pmod/Standalone.pmod/precompile.pike"
#line 1 "/Users/hww3/devel/pike/src/string_builder.cmod"
#line 1 "/Users/hww3/devel/pike/src/string_builder.cmod"
/* -*- mode: c; encoding: utf-8; -*-
|| 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_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 "sprintf.h"
#include "pike_search.h"
#include "fdlib.h"

#include <errno.h>

#define DEFAULT_CMOD_STORAGE



#ifndef PIKE_UNUSED_ATTRIBUTE
#define PIKE_UNUSED_ATTRIBUTE
#endif
#define CMOD_MAP_PROGRAM_IDS_DEFINED 1
static int ___cmod_map_program_ids(int id) PIKE_UNUSED_ATTRIBUTE;
#ifndef TYPEOF
/* Compat with older Pikes. */
#define TYPEOF(SVAL)	((SVAL).type)
#define SUBTYPEOF(SVAL)	((SVAL).subtype)
#define SET_SVAL_TYPE(SVAL, TYPE)	(TYPEOF(SVAL) = TYPE)
#define SET_SVAL_SUBTYPE(SVAL, TYPE)	(SUBTYPEOF(SVAL) = TYPE)
#define SET_SVAL(SVAL, TYPE, SUBTYPE, FIELD, EXPR) do {	\
    /* Set the type afterwards to avoid a clobbered	\
     * svalue in case EXPR throws. */			\
    (SVAL).u.FIELD = (EXPR);				\
    SET_SVAL_TYPE((SVAL), (TYPE));			\
    SET_SVAL_SUBTYPE((SVAL), (SUBTYPE));		\
  } while(0)
#endif /* !TYPEOF */
#ifndef PIKE_UNUSED
/* Compat with Pike 7.8 and earlier. */
/* NB: Not strictly correct; PIKE_UNUSED was added the
 *     day after set_program_id_to_id(), but good enough.
 */
#ifndef UNUSED
#define UNUSED(X)	X
#endif
#ifndef set_program_id_to_id
/* NB: Recent Pike 7.8 has a #define that conflicts with
 *     the following declaration.
 */
static void set_program_id_to_id(void*UNUSED(id)){}
#endif
#else /* */
PMOD_EXPORT void set_program_id_to_id( int (*to)(int) );
#endif /* !PIKE_UNUSED */
#if !defined(string_has_null) && !defined(STRING_CONTENT_CHECKED)
/* This symbol was added as a macro in Pike 7.7.20, and is an
 * inline function in Pike 7.9.4 and later, at which time the
 * flag STRING_CONTENT_CHECKED was added.
 */
#define string_has_null(X) (strlen((X)->str)!=(size_t)(X)->len)
#endif


#ifndef DEFAULT_CMOD_STORAGE
#define CMOD_COND_USED
#define DEFAULT_CMOD_STORAGE static
#endif
#ifndef CMOD_COND_USED
# define Buffer_f_Buffer_cq__search_fun_num_used 1
# define Buffer_program_fun_num_used 1
# define f_Buffer_add_fun_num_used 1
# define f_Buffer_cast_fun_num_used 1
# define f_Buffer_clear_fun_num_used 1
# define f_Buffer_cq__backtick_add_eq_fun_num_used 1
# define f_Buffer_cq__backtick_add_fun_num_used 1
# define f_Buffer_cq__search_1_fun_num_used 1
# define f_Buffer_cq__search_2_fun_num_used 1
# define f_Buffer_cq__size_object_fun_num_used 1
# define f_Buffer_cq__sizeof_fun_num_used 1
# define f_Buffer_cq__sprintf_fun_num_used 1
# define f_Buffer_create_fun_num_used 1
# define f_Buffer_get_copy_fun_num_used 1
# define f_Buffer_get_fun_num_used 1
# define f_Buffer_putchar_fun_num_used 1
# define f_Buffer_sprintf_fun_num_used 1
#else /* !CMOD_COND_USED */
# undef Buffer_f_Buffer_cq__search_fun_num_used
# undef Buffer_program_fun_num_used
# undef f_Buffer_add_fun_num_used
# undef f_Buffer_cast_fun_num_used
# undef f_Buffer_clear_fun_num_used
# undef f_Buffer_cq__backtick_add_eq_fun_num_used
# undef f_Buffer_cq__backtick_add_fun_num_used
# undef f_Buffer_cq__search_1_fun_num_used
# undef f_Buffer_cq__search_2_fun_num_used
# undef f_Buffer_cq__size_object_fun_num_used
# undef f_Buffer_cq__sizeof_fun_num_used
# undef f_Buffer_cq__sprintf_fun_num_used
# undef f_Buffer_create_fun_num_used
# undef f_Buffer_get_copy_fun_num_used
# undef f_Buffer_get_fun_num_used
# undef f_Buffer_putchar_fun_num_used
# undef f_Buffer_sprintf_fun_num_used
#endif /* !CMOD_COND_USED */

#ifdef f_Buffer_cq__size_object_fun_num_used
DEFAULT_CMOD_STORAGE ptrdiff_t f_Buffer_cq__size_object_fun_num = -1;

#endif /* f_Buffer_cq__size_object_fun_num_used */

#ifdef f_Buffer_create_fun_num_used
DEFAULT_CMOD_STORAGE ptrdiff_t f_Buffer_create_fun_num = -1;

#endif /* f_Buffer_create_fun_num_used */

#ifdef f_Buffer_cq__sprintf_fun_num_used
DEFAULT_CMOD_STORAGE ptrdiff_t f_Buffer_cq__sprintf_fun_num = -1;

#endif /* f_Buffer_cq__sprintf_fun_num_used */

#ifdef f_Buffer_cast_fun_num_used
DEFAULT_CMOD_STORAGE ptrdiff_t f_Buffer_cast_fun_num = -1;

#endif /* f_Buffer_cast_fun_num_used */

#ifdef f_Buffer_cq__backtick_add_fun_num_used
DEFAULT_CMOD_STORAGE ptrdiff_t f_Buffer_cq__backtick_add_fun_num = -1;

#endif /* f_Buffer_cq__backtick_add_fun_num_used */

#ifdef f_Buffer_cq__backtick_add_eq_fun_num_used
DEFAULT_CMOD_STORAGE ptrdiff_t f_Buffer_cq__backtick_add_eq_fun_num = -1;

#endif /* f_Buffer_cq__backtick_add_eq_fun_num_used */

#ifdef f_Buffer_add_fun_num_used
DEFAULT_CMOD_STORAGE ptrdiff_t f_Buffer_add_fun_num = -1;

#endif /* f_Buffer_add_fun_num_used */

#ifdef f_Buffer_putchar_fun_num_used
DEFAULT_CMOD_STORAGE ptrdiff_t f_Buffer_putchar_fun_num = -1;

#endif /* f_Buffer_putchar_fun_num_used */

#ifdef f_Buffer_sprintf_fun_num_used
DEFAULT_CMOD_STORAGE ptrdiff_t f_Buffer_sprintf_fun_num = -1;

#endif /* f_Buffer_sprintf_fun_num_used */

#ifdef f_Buffer_get_copy_fun_num_used
DEFAULT_CMOD_STORAGE ptrdiff_t f_Buffer_get_copy_fun_num = -1;

#endif /* f_Buffer_get_copy_fun_num_used */

#ifdef f_Buffer_get_fun_num_used
DEFAULT_CMOD_STORAGE ptrdiff_t f_Buffer_get_fun_num = -1;

#endif /* f_Buffer_get_fun_num_used */

#ifdef f_Buffer_clear_fun_num_used
DEFAULT_CMOD_STORAGE ptrdiff_t f_Buffer_clear_fun_num = -1;

#endif /* f_Buffer_clear_fun_num_used */

#ifdef f_Buffer_cq__sizeof_fun_num_used
DEFAULT_CMOD_STORAGE ptrdiff_t f_Buffer_cq__sizeof_fun_num = -1;

#endif /* f_Buffer_cq__sizeof_fun_num_used */

#ifdef f_Buffer_cq__search_1_fun_num_used
DEFAULT_CMOD_STORAGE ptrdiff_t f_Buffer_cq__search_1_fun_num = -1;

#endif /* f_Buffer_cq__search_1_fun_num_used */

#ifdef f_Buffer_cq__search_2_fun_num_used
DEFAULT_CMOD_STORAGE ptrdiff_t f_Buffer_cq__search_2_fun_num = -1;

#endif /* f_Buffer_cq__search_2_fun_num_used */

#ifdef Buffer_f_Buffer_cq__search_fun_num_used
DEFAULT_CMOD_STORAGE ptrdiff_t Buffer_f_Buffer_cq__search_fun_num = -1;

#endif /* Buffer_f_Buffer_cq__search_fun_num_used */
DEFAULT_CMOD_STORAGE struct program *Buffer_program=NULL;

#ifdef Buffer_program_fun_num_used
DEFAULT_CMOD_STORAGE int Buffer_program_fun_num=-1;
#endif /* Buffer_program_fun_num_used */
#line 32 "/Users/hww3/devel/pike/src/string_builder.cmod"
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 (!s->s) {
    init_string_builder_alloc(s, chars, mag);
    return;
  }
  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?s->s->len:0;
  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 || !s->s->size_shift) {
    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 && (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 (s->s && (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 || !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;
  }

  shift = from.shift;
  if (shift > (s->s ? s->s->size_shift : 0)) {
    /* 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;
}

/**
 * Append a string quoted to Latin-1 (ISO8859-1).
 *
 * @param buf
 *   Buffer to append to.
 *
 * @param str
 *   String to quote.
 *
 * @param i
 *   Starting position of the string.
 *
 * @param max_len
 *   Maximum length of the string. This can be used together with i
 *   to just output a sub-range of the string.
 *
 * @param flags
 *   Set of option flags:
 *
 *   QUOTE_BREAK_AT_DQOUTE
 *     Terminate after the next unescaped double quote ('\"') character.
 *   QUOTE_BREAK_AT_SQUOTE
 *     Terminate after the next unescaped single quote ('\'') character.
 *   QUOTE_BREAK_AT_LF
 *     Terminate after the next unescaped line feed ('\n') character.
 *   QUOTE_NORMALIZE_WS
 *     Combine unescaped sequences of white space characters into
 *     a single space (' ') character.
 *   QUOTE_TOKENIZE
 *     Parse embedded strings.
 *   QUOTE_NO_STRING_CONCAT
 *     Do not insert sequences of two double quotes, and expect them
 *     to be a no-op.
 *
 * CAVEAT EMPTOR:
 *   In old implementations of this function, max_len was the
 *   maximum size of the resulting buffer (including any initial
 *   content).
 */
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)
{
  /* NB: Requires QUOTE_NORMALIZE_WS to be != 1. */
  int inhibit_whitespace = (flags & QUOTE_NORMALIZE_WS);
  int escaped = 0;

  if (str->len < max_len) {
    max_len = str->len;
  }

  for (; i < max_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);
    } 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);
    } else if (ch & 0x60) {
      /* Printable character or DEL. */
      if (ch == '\177' || ch == ' ') {
	/* DEL or SP */
	goto ctrl_char;
      }

      if (ch == '"') {
	string_builder_putchar(buf, '\\');
	string_builder_putchar(buf, '"');
	if (!escaped) {
	  if (flags & QUOTE_BREAK_AT_DQUOTE) {
	    i++;
	    break;
	  }
	  if (flags & QUOTE_TOKENIZE) {
	    i = string_builder_quote_string(buf, str, i+1, max_len,
					    (flags & ~QUOTE_NORMALIZE_WS) |
					    QUOTE_BREAK_AT_DQUOTE);
	  }
	}
	goto next;
      } else if (ch == '\'') {
	string_builder_putchar(buf, '\\');
	string_builder_putchar(buf, '\'');
	if (!escaped) {
	  if (flags & QUOTE_BREAK_AT_SQUOTE) {
	    i++;
	    break;
	  }
	  if (flags & QUOTE_TOKENIZE) {
	    i = string_builder_quote_string(buf, str, i+1, max_len,
					    (flags & ~QUOTE_NORMALIZE_WS) |
					    QUOTE_BREAK_AT_SQUOTE);
	  }
	}
	goto next;
      } else if (ch == '\\') {
	string_builder_putchar(buf, '\\');
	string_builder_putchar(buf, '\\');
	escaped = !escaped;
	inhibit_whitespace = 0;
	goto next_ws;
      }
      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_ws;
      }
      if (ch == ' ') {
	string_builder_putchar(buf, ' ');
	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)) {
	  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:
    inhibit_whitespace = 0;
    escaped = 0;

  next_ws:
    /* A statement is required after a label... */
    ;
  }
  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)
{
  UINT64 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.
 *   'q'	Insert quoted pike_string.
 *   '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)
{
  STACK_LEVEL_START(0);
  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;
	  }
	  /* FALLTHRU */
	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 'q':
	  {
	    struct pike_string *str = va_arg(args, struct pike_string *);
	    size_t len = str->len;
	    size_t buf_len = s->s->len;

	    if (precision && (precision < len)) len = precision;
	    string_builder_putchar(s, '"');
	    string_builder_quote_string(s, str, 0, len, QUOTE_NO_STRING_CONCAT);
	    string_builder_putchar(s, '"');

	    len = s->s->len - buf_len;
	    if (min_width > len) {
	      if (!(flags & APPEND_LEFT)) {
		/* Rewind the buffer so that we can prepend the padding.
		 *
		 * NB: We assume that the quoted string will be
		 *     the same langth the second time too.
		 */
		s->s->len = buf_len;
	      }
	      string_builder_fill(s, min_width - len, MKPCHARP("    ", 0),
				  4, 0);
	      if (!(flags & APPEND_LEFT)) {
		string_builder_putchar(s, '"');
		string_builder_quote_string(s, str, 0, len,
					    QUOTE_NO_STRING_CONCAT);
		string_builder_putchar(s, '"');
	      }
	    }
	  }
	  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);
    }
  }
  STACK_LEVEL_DONE(0);
}


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);
}

/**
 * Append a disassembly-style entry to a string builder buffer.
 *
 * @param s
 *   string builder buffer.
 *
 * @param address
 *   Start address of memory dump for display.
 *
 * @param start
 *   Actual start address for memory dump (if any).
 *
 * @param end
 *   Actual end address for memory dump (if any). NULL to disable
 *   memory dump.
 *
 * @param opcode
 *   C-string for opcode field.
 *
 * @param params
 *   NULL-terminated array of C-strings for parameters field. These
 *   will be comma-separated on output. NULL to disable.
 *
 * @param comment
 *   C-string with comment. NULL to disable. May contain new-lines.
 *
 * Note: To output the address without a memory dump, call with start and
 *       end set to the same non-NULL value.
 *
 * The base format is:
 *
 *   Start Width Use
 *   -------------------------------
 *       0    18 Address
 *      18     2 Space
 *      20    11 Memory dump
 *      31     2 Space
 *      33     8 Opcode
 *      41     1 Space
 *      42    19 Parameters
 *      61     1 Space
 *      62     1 Comment marker (#)
 *      63     1 Space
 *      64    13 Comment
 *
 * The opcode may overflow into the parameters and comment fields.
 */
PMOD_EXPORT void string_builder_append_disassembly(struct string_builder *s,
						   size_t address,
						   const void *start,
						   const void *end,
						   const char *opcode,
						   const char **params,
						   const char *comment)
{
  const unsigned char *pos = start;
  const unsigned char *pos_end = end;

  while ((pos < pos_end) || opcode || (params && params[0]) ||
	 (comment && comment[0])) {
    size_t i;
    int skip_params = 0;
    int skip_comment = 0;

    if (params && !params[0]) params = NULL;
    if (comment && !comment[0]) comment = NULL;

    if (pos_end) {
      /* Address */
      string_builder_sprintf(s, "0x%016lx  ", address);

      if (pos < pos_end) {
	/* Memory dump */
	for (i = 0; i < 4; i++) {
	  if (pos < pos_end) {
	    string_builder_sprintf(s, "%02x ", *pos);
	    pos++;
	    address++;
	    if (pos >= pos_end) {
	      pos_end = NULL;
	    }
	  } else {
	    string_builder_sprintf(s, "   ");
	  }
	}
      } else {
	pos_end = NULL;
	string_builder_sprintf(s, "%*s", 4*3, "");
      }
    } else {
      string_builder_sprintf(s, "%*s", 18 + 2 + 4*3, "");
    }

    /* Opcode */
    if (opcode) {
      char *opcode_end = strchr(opcode, '\n');
      size_t opcode_len;
      if (opcode_end) {
	opcode_len = opcode_end - opcode;
	opcode_end++;
	skip_params = 1;
      } else {
	opcode_len = strlen(opcode);
      }
      if (!params && !comment) {
	/* Nothing else on the line. */
	string_builder_sprintf(s, " %.*s", opcode_len, opcode);
      } else if (opcode_len < 8) {
	string_builder_sprintf(s, " %.*s%*s",
			       opcode_len, opcode, 8 - opcode_len, "");
      } else if (opcode_len < 29) {
	string_builder_sprintf(s, " %.*s%*s",
			       opcode_len, opcode, 28 - opcode_len, "");
	skip_params = 1;
      } else {
	string_builder_sprintf(s, " %.*s", opcode_len, opcode);
	skip_params = skip_comment = 1;
      }
      opcode = opcode_end;
    } else if (params || comment) {
      /* No need to pad if there's no argument and no comment. */
      string_builder_sprintf(s, " %8s ", "");
    }

    /* Params */
    if (!skip_params) {
      ptrdiff_t bytes_left = 20;
      if (params) {
	do {
	  string_builder_sprintf(s, " %s", params[0]);
	  bytes_left -= strlen(params[0]) + 1;
	  params++;
	  if (params[0]) {
	    string_builder_sprintf(s, ",");
	    bytes_left--;
	  }
	} while (params[0] && (((ptrdiff_t)strlen(params[0])) <= bytes_left));
      }
      if (bytes_left < 0) {
	skip_comment = 1;
      } else if (comment) {
	/* No need to pad if there's no comment. */
	string_builder_sprintf(s, "%*s", bytes_left, "");
      }
    }

    /* Comment */
    if (!skip_comment && comment) {
      const char *ptr = strchr(comment, '\n');
      if (ptr) {
	string_builder_sprintf(s, " # %.*s\n", ptr - comment, comment);
	comment = ptr + 1;
	if (!comment[0]) comment = NULL;
      } else {
	string_builder_sprintf(s, " # %s\n", comment);
	comment = NULL;
      }
    } else {
      string_builder_sprintf(s, "\n");
    }
  }
}

PMOD_EXPORT void string_builder_append_disassembly_data(struct string_builder *s,
							size_t address,
							const void *start,
							const void *end,
							const char *comment)
{
  const unsigned char *pos = start;
  const unsigned char *pos_end = end;

  while ((pos < pos_end) || (comment && comment[0])) {
    size_t i;
    int skip_comment = 0;

    if (comment && !comment[0]) comment = NULL;

    if (pos_end) {
      /* Address */
      string_builder_sprintf(s, "0x%016lx  ", address);

      if (pos < pos_end) {
	struct pike_string tmp;
	ptrdiff_t buf_len;

	/* Fake a pike_string for use with %q further below. */
	tmp.refs = 1;
	tmp.flags = 0;
	tmp.size_shift = 0;
	tmp.alloc_type = STRING_ALLOC_STATIC;
	tmp.struct_type = STRING_STRUCT_SUBSTRING;
	tmp.min = 0;
	tmp.max = 255;
	tmp.len = 0;		/* Will be increased in the loop below. */
	tmp.hval = 0;
	tmp.next = NULL;
	tmp.str = (char *)pos;

	/* Memory dump */
	for (i = 0; i < 4; i++) {
	  if (pos < pos_end) {
	    string_builder_sprintf(s, "%02x ", *pos);
	    pos++;
	    tmp.len++;
	    address++;
	    if (pos >= pos_end) {
	      pos_end = NULL;
	    }
	  } else {
	    string_builder_sprintf(s, "   ");
	  }
	}

	buf_len = s->s->len;
	if (comment) {
	  string_builder_sprintf(s, " .data    %-20q", &tmp);
	  if (UNLIKELY(s->s->len > buf_len + 10 + 20)) {
	    /* Paranoia: This probably can't happen.
	     *           Max should be with octal or hex quoting:
	     *             " .data    "   10 +
	     *             "\""            1 +
	     *             "\xxx" * 4    4*4 +
	     *             "\""            1
	     *           ==> 28.
	     */
	    skip_comment = 1;
	  }
	} else {
	  string_builder_sprintf(s, " .data    %q", &tmp);
	}
      } else if (comment) {
	pos_end = NULL;
	string_builder_sprintf(s, "%*s", 4*3 + 10 + 20, "");
      }
    } else {
      string_builder_sprintf(s, "%*s", 18 + 2 + 4*3 + 10 + 20, "");
    }

    /* Comment */
    if (!skip_comment && (comment && comment[0])) {
      const char *ptr = strchr(comment, '\n');
      if (ptr) {
	string_builder_sprintf(s, " # %.*s\n", ptr - comment, comment);
	comment = ptr + 1;
	if (!comment[0]) comment = NULL;
      } else {
	string_builder_sprintf(s, " # %s\n", comment);
	comment = NULL;
      }
    } else {
      string_builder_sprintf(s, "\n");
    }
  }
}

PMOD_EXPORT void string_builder_vsprintf_disassembly(struct string_builder *s,
						     size_t address,
						     const void *start,
						     const void *end,
						     const char *comment,
						     const char *fmt,
						     va_list args)
{
  struct string_builder tmp_buf;
  init_string_builder(&tmp_buf, 0);
  string_builder_vsprintf(&tmp_buf, fmt, args);
  string_builder_putchar(&tmp_buf, 0);

  if (tmp_buf.s->size_shift) {
    Pike_fatal("Wide characters are not yet supported here.\n");
  }

  string_builder_append_disassembly(s, address, start, end,
				    tmp_buf.s->str, NULL, comment);

  free_string_builder(&tmp_buf);
}

PMOD_EXPORT void string_builder_sprintf_disassembly(struct string_builder *s,
						    size_t address,
						    const void *start,
						    const void *end,
						    const char *comment,
						    const char *fmt, ...)
{
  va_list args;
  va_start(args, fmt);
  string_builder_vsprintf_disassembly(s, address, start, end,
				      comment, 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);
  s->s = NULL;
}

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);
}

/**
 * Write the contents of a string_builder buffer to an fd.
 *
 * @param fd
 *   File descriptor to write to.
 *
 * @param s
 *   String builder buffer to write.
 *
 * @returns
 *   Returns the number of bytes written.
 *
 * Removes the data that was written from the buffer.
 */
PMOD_EXPORT ptrdiff_t write_and_reset_string_builder(int fd,
						     struct string_builder *s)
{
  ptrdiff_t bytes = 0;
  if (s && s->s && s->s->len) {
    if (s->s->size_shift) {
      Pike_fatal("Buffer contains wide characters.\n");
    }
    bytes = fd_write(fd, s->s->str, s->s->len);
    if (bytes <= 0) {
    } else if (bytes < s->s->len) {
      s->s->len -= bytes;
      memmove(s->s->str, s->s->str + bytes, s->s->len);
    } else {
      reset_string_builder(s);
    }
  }
  return bytes;
}

/*! @module String
 */

#define PROG_BUFFER_ID	PROG_STRING_BUFFER_ID

/*! @class Buffer
 *!    A buffer, used for building strings. It's
 *!    conceptually similar to a string, but you can only @[add]
 *!    strings to it, and you can only @[get] the value from it once.
 *!
 *!    There is a reason for those seemingly rather odd limitations,
 *!    it makes it possible to do some optimizations that really speed
 *!    things up.
 *!
 *!    You do not need to use this class unless you add very many
 *!    strings together, or very large strings.
 *!
 *! @example
 *!    For the fastest possible operation, write your code like this:
 *!
 *! @code
 *! String.Buffer b = String.Buffer( );
 *!
 *! function add = b->add;
 *!
 *! .. call add several times in code ...
 *!
 *! string result = b->get(); // also clears the buffer
 *! @endcode
 */

#undef class_Buffer_defined
#define class_Buffer_defined

#undef var_str_Buffer_defined
#define var_str_Buffer_defined

#undef var_initial_Buffer_defined
#define var_initial_Buffer_defined

#undef THIS
#define THIS ((struct Buffer_struct *)(Pike_interpreter.frame_pointer->current_storage + Buffer_storage_offset))

#undef THIS_BUFFER
#define THIS_BUFFER ((struct Buffer_struct *)(Pike_interpreter.frame_pointer->current_storage + Buffer_storage_offset))

#undef OBJ2_BUFFER
#define OBJ2_BUFFER(o) ((struct Buffer_struct *)(o->storage+Buffer_storage_offset+Buffer_program->inherits[0].storage_offset))

#undef GET_BUFFER_STORAGE
#define GET_BUFFER_STORAGE(o) ((struct Buffer_struct *)(o->storage+Buffer_storage_offset+Buffer_program->inherits[0].storage_offset))
static ptrdiff_t Buffer_storage_offset;
struct Buffer_struct {

#ifdef var_str_Buffer_defined
#line 1440 "/Users/hww3/devel/pike/src/string_builder.cmod"
struct string_builder str;
#endif /* var_str_Buffer_defined */

#ifdef var_initial_Buffer_defined
#line 1441 "/Users/hww3/devel/pike/src/string_builder.cmod"
int initial;
#endif /* var_initial_Buffer_defined */
};
#ifdef PIKE_DEBUG
/* Ensure the struct is used in a variable declaration, or else gdb might not see it. */
static struct Buffer_struct *Buffer_gdb_dummy_ptr;
#endif
#define f_Buffer_cq__size_object_defined
DEFAULT_CMOD_STORAGE void f_Buffer_cq__size_object(INT32 args) {
#line 1443 "/Users/hww3/devel/pike/src/string_builder.cmod"
if(args != 0) wrong_number_of_args_error("_size_object",args,0);
{
      if( THIS->str.s )
          do { INT_TYPE ret_=(THIS->str.malloced);  push_int(ret_); return; }while(0);do { INT_TYPE ret_=(
#line 1447 "/Users/hww3/devel/pike/src/string_builder.cmod"
0);  push_int(ret_); return; }while(0);
#line 1448 "/Users/hww3/devel/pike/src/string_builder.cmod"
}

  }
#line 1450 "/Users/hww3/devel/pike/src/string_builder.cmod"
void f_Buffer_get_copy( INT32 args );
  void f_Buffer_get( INT32 args );
  void f_Buffer_add( INT32 args );

  /*! @decl void create(int initial_size)
   *!
   *!   Initializes a new buffer.
   *!
   *!   If no @[initial_size] is specified, 256 is used. If you
   *!   know approximately how big the buffer will be, you can optimize
   *!   the operation of @[add()] (slightly) by passing the size to this
   *!   function.
   */
  #define f_Buffer_create_defined
DEFAULT_CMOD_STORAGE void f_Buffer_create(INT32 args) {
#line 1463 "/Users/hww3/devel/pike/src/string_builder.cmod"
struct svalue * size;
#ifdef PIKE_DEBUG
if(args < 0) Pike_fatal("CMOD function argument negative.\n");
#else
STATIC_ASSUME(args >= 0);
#endif
#line 1463 "/Users/hww3/devel/pike/src/string_builder.cmod"
if(args > 1) wrong_number_of_args_error("create",args,1);
#line 1463 "/Users/hww3/devel/pike/src/string_builder.cmod"
if ((args > 0) &&
    ((TYPEOF(Pike_sp[0-args]) != PIKE_T_INT) ||
     (SUBTYPEOF(Pike_sp[0-args]) != NUMBER_UNDEFINED))) {
#line 1463 "/Users/hww3/devel/pike/src/string_builder.cmod"
if((TYPEOF(Pike_sp[0-args]) != PIKE_T_INT)) SIMPLE_ARG_TYPE_ERROR("create",1,"int|void");
#line 1463 "/Users/hww3/devel/pike/src/string_builder.cmod"
size=Pike_sp+0-args; dmalloc_touch_svalue(Pike_sp+0-args);
#line 1463 "/Users/hww3/devel/pike/src/string_builder.cmod"
} else size = NULL;
{
    struct Buffer_struct *str = THIS;
    if( size )
      str->initial = MAXIMUM( size->u.integer, 512 );
    else
      str->initial = 256;
    pop_n_elems(args);
  }

  }
/*! @decl string _sprintf( int flag, mapping flags )
   *! It is possible to @[sprintf] a String.Buffer object
   *! as @tt{%s@} just as if it was a string.
   */
  #define f_Buffer_cq__sprintf_defined
DEFAULT_CMOD_STORAGE void f_Buffer_cq__sprintf(INT32 args) {
#line 1477 "/Users/hww3/devel/pike/src/string_builder.cmod"
INT_TYPE flag;
#line 1477 "/Users/hww3/devel/pike/src/string_builder.cmod"
struct mapping * flags;
#line 1477 "/Users/hww3/devel/pike/src/string_builder.cmod"
if(args != 2) wrong_number_of_args_error("_sprintf",args,2);
#line 1477 "/Users/hww3/devel/pike/src/string_builder.cmod"
if((TYPEOF(Pike_sp[0-2]) != PIKE_T_INT)) SIMPLE_ARG_TYPE_ERROR("_sprintf",1,"int");
#line 1477 "/Users/hww3/devel/pike/src/string_builder.cmod"
flag=Pike_sp[0-2].u.integer;
#line 1477 "/Users/hww3/devel/pike/src/string_builder.cmod"
if(TYPEOF(Pike_sp[1-2]) != PIKE_T_MAPPING) SIMPLE_ARG_TYPE_ERROR("_sprintf",2,"mapping(mixed:mixed)");
#line 1477 "/Users/hww3/devel/pike/src/string_builder.cmod"
debug_malloc_pass(flags=Pike_sp[1-2].u.mapping);
{
    switch( flag )
    {
      case 'O':
	{
	  struct pike_string *res;
	  struct Buffer_struct *str = THIS;
	  push_static_text( "Buffer(%d /* %d */)" );
	  if( str->str.s )
	  {
	    push_int(str->str.s->len);
	    push_int(str->str.malloced);
	  }
	  else
	  {
	    push_int( 0 );
	    push_int( 0 );
	  }
	  f_sprintf( 3 );
	  dmalloc_touch_svalue(Pike_sp-1);
	  res = Pike_sp[-1].u.string;
	  Pike_sp--;
	  do { struct pike_string * ret_=(res); pop_n_elems(2); push_string(ret_); return; }while(0);
#line 1501 "/Users/hww3/devel/pike/src/string_builder.cmod"
}

      case 's':
	{
	  pop_n_elems( args );
	  if( Pike_fp->current_object->refs != 1 )
	    f_Buffer_get_copy( 0 );
	  else
	    f_Buffer_get( 0 );
	}
	return;

      case 't':
	do { struct pike_string * ret_=(make_shared_binary_string("Buffer",6)); pop_n_elems(2); push_string(ret_); return; }while(0);
#line 1515 "/Users/hww3/devel/pike/src/string_builder.cmod"
}
    pop_n_elems( args );
    push_undefined();
  }

  }
/*! @decl mixed cast( string type )
   *! It is possible to cast a String.Buffer object to
   *! a @expr{string@} and an @expr{int@}.
   */
  #define f_Buffer_cast_defined
DEFAULT_CMOD_STORAGE void f_Buffer_cast(INT32 args) {
#line 1524 "/Users/hww3/devel/pike/src/string_builder.cmod"
struct pike_string * type;
#line 1524 "/Users/hww3/devel/pike/src/string_builder.cmod"
if(args != 1) wrong_number_of_args_error("cast",args,1);
#line 1524 "/Users/hww3/devel/pike/src/string_builder.cmod"
if(TYPEOF(Pike_sp[0-1]) != PIKE_T_STRING) SIMPLE_ARG_TYPE_ERROR("cast",1,"string");
#line 1524 "/Users/hww3/devel/pike/src/string_builder.cmod"
debug_malloc_pass(type=Pike_sp[0-1].u.string);
#line 1526 "/Users/hww3/devel/pike/src/string_builder.cmod"
{
    if( type == literal_string_string )
    {
      pop_stack();
      if( Pike_fp->current_object->refs != 1 )
	f_Buffer_get_copy( 0 );
      else
	f_Buffer_get( 0 );
      return;
    }

    if( type == literal_int_string )
    {
      struct Buffer_struct *str = THIS;
      pop_stack();
      if( Pike_fp->current_object->refs != 1 )
	f_Buffer_get_copy( 0 );
      else
	f_Buffer_get( 0 );
      o_cast_to_int( );
      return;
    }

    pop_stack();
    push_undefined();
  }

  }
/*! @decl String.Buffer `+( string|String.Buffer what )
   */
  #define f_Buffer_cq__backtick_add_defined
DEFAULT_CMOD_STORAGE void f_Buffer_cq__backtick_add(INT32 args) {
#line 1555 "/Users/hww3/devel/pike/src/string_builder.cmod"
struct svalue * what;
#line 1555 "/Users/hww3/devel/pike/src/string_builder.cmod"
if(args != 1) wrong_number_of_args_error("`+",args,1);
#line 1555 "/Users/hww3/devel/pike/src/string_builder.cmod"
what=Pike_sp+0-1; dmalloc_touch_svalue(Pike_sp+0-1);
#line 1557 "/Users/hww3/devel/pike/src/string_builder.cmod"
{
    struct Buffer_struct *str = THIS, *str2;
    struct object *res = fast_clone_object( Buffer_program );
    str2 = OBJ2_BUFFER( res );
    str2->initial = str->initial;
    if( str->str.s )
      init_string_builder_copy (&str2->str, &str->str);
    if( (Pike_fp->current_object->flags & OBJECT_CLEAR_ON_EXIT) )
        res->flags |= OBJECT_CLEAR_ON_EXIT;
    apply( res, "add", 1 );
    do { struct object * ret_=(res); pop_stack(); push_object(ret_); return; }while(0);
#line 1568 "/Users/hww3/devel/pike/src/string_builder.cmod"
}

  }
/*! @decl String.Buffer `+=( string|String.Buffer what )
   */
  #define f_Buffer_cq__backtick_add_eq_defined
DEFAULT_CMOD_STORAGE void f_Buffer_cq__backtick_add_eq(INT32 args) {
#line 1572 "/Users/hww3/devel/pike/src/string_builder.cmod"
struct svalue * what;
#line 1572 "/Users/hww3/devel/pike/src/string_builder.cmod"
if(args != 1) wrong_number_of_args_error("`+=",args,1);
#line 1572 "/Users/hww3/devel/pike/src/string_builder.cmod"
what=Pike_sp+0-1; dmalloc_touch_svalue(Pike_sp+0-1);
#line 1574 "/Users/hww3/devel/pike/src/string_builder.cmod"
{
    f_Buffer_add( 1 );
    do { struct object * ret_=(Pike_fp->current_object); add_ref(ret_); pop_stack(); push_object(ret_); return; }while(0);
#line 1577 "/Users/hww3/devel/pike/src/string_builder.cmod"
}

  }
#line 1579 "/Users/hww3/devel/pike/src/string_builder.cmod"
/**
   * Return the string_builder that is embedded in
   * a String.Buffer.
   */
  PMOD_EXPORT struct string_builder *
    string_builder_from_string_buffer(struct object *o)
  {
    if (!o) return NULL;

    if (LIKELY(o->prog == Buffer_program))
      return &OBJ2_BUFFER(o)->str;

    return &((struct Buffer_struct *)get_storage(o, Buffer_program))->str;
  }

  /*! @decl int add(string|String.Buffer ... data)
   *!
   *!   Adds @[data] to the buffer.
   *!
   *! @returns
   *!   Returns the size of the buffer.
   *!
   *! @note
   *!   Pike 7.8 and earlier did not support adding @[String.Buffer]s
   *!   directly.
   */
  #define f_Buffer_add_defined
DEFAULT_CMOD_STORAGE void f_Buffer_add(INT32 args) {
#line 1605 "/Users/hww3/devel/pike/src/string_builder.cmod"
struct svalue * arg1;
#ifdef PIKE_DEBUG
if(args < 0) Pike_fatal("CMOD function argument negative.\n");
#else
STATIC_ASSUME(args >= 0);
#endif
#line 1605 "/Users/hww3/devel/pike/src/string_builder.cmod"
if (args > 0) {
  INT32 argcnt = 0;
  do {
    dmalloc_touch_svalue(Pike_sp+0+argcnt-args);
#line 1605 "/Users/hww3/devel/pike/src/string_builder.cmod"
  } while (++argcnt < args-0);
  arg1=Pike_sp+0-args;
} else arg1 = NULL;
#line 1607 "/Users/hww3/devel/pike/src/string_builder.cmod"
{
    struct Buffer_struct *str = THIS;

    if (args) {
      int init_from_arg0 = 0, j;
      ptrdiff_t sum = 0;
      int shift = 0;
      for (j=0; j < args; j++) {
	struct pike_string *a;
	if (TYPEOF(Pike_sp[j-args]) == PIKE_T_STRING)
	  a = Pike_sp[j-args].u.string;
	else if ((TYPEOF(Pike_sp[j-args]) != PIKE_T_OBJECT) ||
		   (Pike_sp[j-args].u.object->prog != Buffer_program))
	  SIMPLE_ARG_TYPE_ERROR("add", j+1, "string|String.Buffer");
	else {
	  a = OBJ2_BUFFER(Pike_sp[j-args].u.object)->str.s;
	  if (!a) continue;
	}
	sum += a->len;
	shift |= a->size_shift;
      }
      shift |= str->str.known_shift;
      shift = shift & ~(shift >> 1);
      /* We know it will be a string that really is this wide. */
      str->str.known_shift = shift;

      if (!str->str.s) {
	if (sum <= str->initial)
	  sum = str->initial;
	else
	  sum <<= 1;

	init_string_builder_alloc(&str->str, sum, shift);
      } else
	string_build_mkspace(&str->str, sum, shift);

      for(sum = str->str.s->len, j = 0; j<args; j++) {
	struct pike_string *a;
	if (TYPEOF(Pike_sp[j-args]) == PIKE_T_STRING)
	  a = Pike_sp[j-args].u.string;
	else {
	  a = OBJ2_BUFFER(Pike_sp[j-args].u.object)->str.s;
	  if (!a) continue;
	}
	pike_string_cpy(MKPCHARP_STR_OFF(str->str.s, sum), a);
	sum += a->len;
      }

      str->str.s->len = sum;
    }

    do { INT_TYPE ret_=(str->str.s ? str->str.s->len : 0); pop_n_elems(args); push_int(ret_); return; }while(0);
#line 1659 "/Users/hww3/devel/pike/src/string_builder.cmod"
}

  }
/*! @decl void putchar(int c)
   *! Appends the character @[c] at the end of the string.
   */
  #define f_Buffer_putchar_defined
DEFAULT_CMOD_STORAGE void f_Buffer_putchar(INT32 args) {
#line 1664 "/Users/hww3/devel/pike/src/string_builder.cmod"
INT_TYPE c;
#line 1664 "/Users/hww3/devel/pike/src/string_builder.cmod"
if(args != 1) wrong_number_of_args_error("putchar",args,1);
#line 1664 "/Users/hww3/devel/pike/src/string_builder.cmod"
if((TYPEOF(Pike_sp[0-1]) != PIKE_T_INT)) SIMPLE_ARG_TYPE_ERROR("putchar",1,"int");
#line 1664 "/Users/hww3/devel/pike/src/string_builder.cmod"
c=Pike_sp[0-1].u.integer;
#line 1664 "/Users/hww3/devel/pike/src/string_builder.cmod"
{
    struct Buffer_struct *str = THIS;
    if(!str->str.s)
      init_string_builder_alloc(&str->str, str->initial, 0);
    string_builder_putchar(&str->str, c);
  }

  }
/*! @decl int sprintf(strict_sprintf_format format, sprintf_args ... args)
   *! Appends the output from @[sprintf] at the end of the string.
   *! Returns the resulting size of the String.Buffer.
   */
  #define f_Buffer_sprintf_defined
DEFAULT_CMOD_STORAGE void f_Buffer_sprintf(INT32 args) {
#line 1675 "/Users/hww3/devel/pike/src/string_builder.cmod"
struct svalue * arguments;
#ifdef PIKE_DEBUG
if(args < 0) Pike_fatal("CMOD function argument negative.\n");
#else
STATIC_ASSUME(args >= 0);
#endif
#line 1675 "/Users/hww3/devel/pike/src/string_builder.cmod"
if (args > 0) {
  INT32 argcnt = 0;
  do {
    dmalloc_touch_svalue(Pike_sp+0+argcnt-args);
#line 1675 "/Users/hww3/devel/pike/src/string_builder.cmod"
  } while (++argcnt < args-0);
  arguments=Pike_sp+0-args;
} else arguments = NULL;
#line 1679 "/Users/hww3/devel/pike/src/string_builder.cmod"
{
    /* FIXME: Reset length on exception? */
    struct Buffer_struct *str = THIS;
    if(!str->str.s)
      init_string_builder_alloc(&str->str, str->initial, 0);
    low_f_sprintf(args, &str->str);
    do { INT_TYPE ret_=(str->str.s->len); pop_n_elems(args); push_int(ret_); return; }while(0);
#line 1686 "/Users/hww3/devel/pike/src/string_builder.cmod"
}

  }
/*! @decl string get_copy()
   *!
   *!   Get the data from the buffer. Significantly slower than @[get],
   *!   but does not clear the buffer.
   *!
   *! @seealso
   *!   @[get()]
   */
  #define f_Buffer_get_copy_defined
DEFAULT_CMOD_STORAGE void f_Buffer_get_copy(INT32 args) {
#line 1696 "/Users/hww3/devel/pike/src/string_builder.cmod"
if(args != 0) wrong_number_of_args_error("get_copy",args,0);
{
    struct pike_string *str = THIS->str.s;
    ptrdiff_t len;
    if( str && (len = str->len) > 0 )
    {
      char *d = (char *)str->str;
      switch( str->size_shift )
      {
        case 0:
          str=make_shared_binary_string0((p_wchar0 *)d,len);
          break;
        case 1:
          str=make_shared_binary_string1((p_wchar1 *)d,len);
          break;
        case 2:
          str=make_shared_binary_string2((p_wchar2 *)d,len);
          break;
      }
      if( Pike_fp->current_object->flags & OBJECT_CLEAR_ON_EXIT )
          str->flags |= STRING_CLEAR_ON_EXIT;
      do { struct pike_string * ret_=(str);  push_string(ret_); return; }while(0);
#line 1718 "/Users/hww3/devel/pike/src/string_builder.cmod"
}
    push_empty_string();
    return;
  }

  }
/*! @decl string get()
   *!
   *!   Get the data from the buffer.
   *!
   *! @note
   *!   This will clear the data in the buffer
   *!
   *! @seealso
   *!   @[get_copy()], @[clear()]
   */
  #define f_Buffer_get_defined
DEFAULT_CMOD_STORAGE void f_Buffer_get(INT32 args) {
#line 1733 "/Users/hww3/devel/pike/src/string_builder.cmod"
if(args != 0) wrong_number_of_args_error("get",args,0);
{
    struct Buffer_struct *str = THIS;
    pop_n_elems(args);
    if( str->str.s )
    {
      struct pike_string *s = finish_string_builder( &str->str );
      str->str.malloced = 0;
      str->str.s = NULL;
      if( Pike_fp->current_object->flags & OBJECT_CLEAR_ON_EXIT )
          s->flags |= STRING_CLEAR_ON_EXIT;
      push_string(s);
    }
    else
      push_empty_string();
  }

  }
/*! @decl void clear()
   *!
   *!   Empty the buffer, and don't care about the old content.
   *!
   *! @note
   *!   This function was not available in Pike 7.8 and earlier.
   *!
   *! @seealso
   *!   @[get()]
   */
  #define f_Buffer_clear_defined
DEFAULT_CMOD_STORAGE void f_Buffer_clear(INT32 args) {
#line 1760 "/Users/hww3/devel/pike/src/string_builder.cmod"
if(args != 0) wrong_number_of_args_error("clear",args,0);
{
    /* FIXME: Support resetting the initial size? */
    struct Buffer_struct *str = THIS;
    if (str->str.s) {
      /* FIXME: There's also the alternative of using
       *        reset_string_builder() here.
       */
      free_string_builder(&str->str);
      str->str.s = NULL;
    }
  }

  }
/*! @decl int _sizeof()
   *!
   *!   Returns the size of the buffer.
   */
  #define f_Buffer_cq__sizeof_defined
DEFAULT_CMOD_STORAGE void f_Buffer_cq__sizeof(INT32 args) {
#line 1777 "/Users/hww3/devel/pike/src/string_builder.cmod"
if(args != 0) wrong_number_of_args_error("_sizeof",args,0);
#line 1779 "/Users/hww3/devel/pike/src/string_builder.cmod"
{
    struct Buffer_struct *str = THIS;
    do { INT_TYPE ret_=(str->str.s ? str->str.s->len : 0);  push_int(ret_); return; }while(0);
#line 1782 "/Users/hww3/devel/pike/src/string_builder.cmod"
}

  }
/*! @decl int(0..) _search(int character, int|void start, int|void end)
   *!
   *! Search for a @[character] in the buffer, starting the scan
   *! from @[start] and ending at @[end] (inclusive).
   *!
   *! @returns
   *!   Returns to position in the buffer where the character was found
   *!   on success, and @[UNDEFINED] on failure.
   *!
   *! @seealso
   *!   @[Stdio.Buffer()->_search()], @[search()], @[lfun::_search()]
   */
  #define f_Buffer_cq__search_1_defined
DEFAULT_CMOD_STORAGE void f_Buffer_cq__search_1(INT32 args) {
#line 1796 "/Users/hww3/devel/pike/src/string_builder.cmod"
INT_TYPE character;
#line 1796 "/Users/hww3/devel/pike/src/string_builder.cmod"
struct svalue * start;
#line 1796 "/Users/hww3/devel/pike/src/string_builder.cmod"
struct svalue * end;
#line 1796 "/Users/hww3/devel/pike/src/string_builder.cmod"
if(args < 1) wrong_number_of_args_error("_search_1",args,1);
#line 1796 "/Users/hww3/devel/pike/src/string_builder.cmod"
if(args > 3) wrong_number_of_args_error("_search_1",args,3);
#line 1796 "/Users/hww3/devel/pike/src/string_builder.cmod"
if((TYPEOF(Pike_sp[0-args]) != PIKE_T_INT)) SIMPLE_ARG_TYPE_ERROR("_search",1,"int");
#line 1796 "/Users/hww3/devel/pike/src/string_builder.cmod"
character=Pike_sp[0-args].u.integer;
#line 1796 "/Users/hww3/devel/pike/src/string_builder.cmod"
if ((args > 1) &&
    ((TYPEOF(Pike_sp[1-args]) != PIKE_T_INT) ||
     (SUBTYPEOF(Pike_sp[1-args]) != NUMBER_UNDEFINED))) {
#line 1796 "/Users/hww3/devel/pike/src/string_builder.cmod"
if((TYPEOF(Pike_sp[1-args]) != PIKE_T_INT)) SIMPLE_ARG_TYPE_ERROR("_search",2,"int|void");
#line 1796 "/Users/hww3/devel/pike/src/string_builder.cmod"
start=Pike_sp+1-args; dmalloc_touch_svalue(Pike_sp+1-args);
#line 1796 "/Users/hww3/devel/pike/src/string_builder.cmod"
} else start = NULL;
#line 1796 "/Users/hww3/devel/pike/src/string_builder.cmod"
if ((args > 2) &&
    ((TYPEOF(Pike_sp[2-args]) != PIKE_T_INT) ||
     (SUBTYPEOF(Pike_sp[2-args]) != NUMBER_UNDEFINED))) {
#line 1796 "/Users/hww3/devel/pike/src/string_builder.cmod"
if((TYPEOF(Pike_sp[2-args]) != PIKE_T_INT)) SIMPLE_ARG_TYPE_ERROR("_search",3,"int|void");
#line 1796 "/Users/hww3/devel/pike/src/string_builder.cmod"
end=Pike_sp+2-args; dmalloc_touch_svalue(Pike_sp+2-args);
#line 1796 "/Users/hww3/devel/pike/src/string_builder.cmod"
} else end = NULL;
{
    PCHARP buf = MKPCHARP_STR(THIS->str.s);
    ptrdiff_t len = THIS->str.s->len;
    ptrdiff_t i;

    if (end && (end->u.integer + 1 < len)) {
      len = end->u.integer + 1;
    }
    if (len > 0 && start && (start->u.integer > 0)) {
      INC_PCHARP(buf, start->u.integer);
      len -= start->u.integer;
    }
    if (len <= 0) {
      push_int(-1);
      return;
    }
    if (UNLIKELY(THIS->str.s->size_shift == thirtytwobit)) {
#if SIZEOF_INT_TYPE > 4
      if (UNLIKELY((0x7fffffff < character) || (character < -0x80000000))) {
        push_int(-1);
	return;
      }
#endif
    } else if (UNLIKELY((1L<<(8<<THIS->str.s->size_shift)) <= character) ||
	       UNLIKELY(character < 0)) {
      push_int(-1);
      return;
    }
    for (i = 0; i < len; i++) {
      p_wchar2 c = INDEX_PCHARP(buf, i);
      if (c == character) {
        if(start)
          i += start->u.integer;
	push_int64(i);
	return;
      }
    }
    push_int(-1);
  }

  }
/*! @decl int(0..) _search(string substring, int|void start, int|void end)
   *!
   *! Search for a @[substring] in the buffer, starting the scan
   *! from @[start] and ending at @[end] (inclusive).
   *!
   *! @returns
   *!   Returns to position in the buffer where the substring was found
   *!   on success, and @[UNDEFINED] on failure.
   *!
   *! @seealso
   *!   @[Stdio.Buffer()->_search()], @[search()], @[lfun::_search()]
   */
  #define f_Buffer_cq__search_2_defined
DEFAULT_CMOD_STORAGE void f_Buffer_cq__search_2(INT32 args) {
#line 1849 "/Users/hww3/devel/pike/src/string_builder.cmod"
struct pike_string * substring;
#line 1849 "/Users/hww3/devel/pike/src/string_builder.cmod"
struct svalue * start;
#line 1849 "/Users/hww3/devel/pike/src/string_builder.cmod"
struct svalue * end;
#line 1849 "/Users/hww3/devel/pike/src/string_builder.cmod"
if(args < 1) wrong_number_of_args_error("_search_2",args,1);
#line 1849 "/Users/hww3/devel/pike/src/string_builder.cmod"
if(args > 3) wrong_number_of_args_error("_search_2",args,3);
#line 1849 "/Users/hww3/devel/pike/src/string_builder.cmod"
if(TYPEOF(Pike_sp[0-args]) != PIKE_T_STRING) SIMPLE_ARG_TYPE_ERROR("_search",1,"string");
#line 1849 "/Users/hww3/devel/pike/src/string_builder.cmod"
debug_malloc_pass(substring=Pike_sp[0-args].u.string);
#line 1849 "/Users/hww3/devel/pike/src/string_builder.cmod"
if ((args > 1) &&
    ((TYPEOF(Pike_sp[1-args]) != PIKE_T_INT) ||
     (SUBTYPEOF(Pike_sp[1-args]) != NUMBER_UNDEFINED))) {
#line 1849 "/Users/hww3/devel/pike/src/string_builder.cmod"
if((TYPEOF(Pike_sp[1-args]) != PIKE_T_INT)) SIMPLE_ARG_TYPE_ERROR("_search",2,"int|void");
#line 1849 "/Users/hww3/devel/pike/src/string_builder.cmod"
start=Pike_sp+1-args; dmalloc_touch_svalue(Pike_sp+1-args);
#line 1849 "/Users/hww3/devel/pike/src/string_builder.cmod"
} else start = NULL;
#line 1849 "/Users/hww3/devel/pike/src/string_builder.cmod"
if ((args > 2) &&
    ((TYPEOF(Pike_sp[2-args]) != PIKE_T_INT) ||
     (SUBTYPEOF(Pike_sp[2-args]) != NUMBER_UNDEFINED))) {
#line 1849 "/Users/hww3/devel/pike/src/string_builder.cmod"
if((TYPEOF(Pike_sp[2-args]) != PIKE_T_INT)) SIMPLE_ARG_TYPE_ERROR("_search",3,"int|void");
#line 1849 "/Users/hww3/devel/pike/src/string_builder.cmod"
end=Pike_sp+2-args; dmalloc_touch_svalue(Pike_sp+2-args);
#line 1849 "/Users/hww3/devel/pike/src/string_builder.cmod"
} else end = NULL;
{
    PCHARP buf = MKPCHARP_STR(THIS->str.s);
    ptrdiff_t len = THIS->str.s->len;
    ptrdiff_t i;
    SearchMojt mojt;
    PCHARP res;

    if (end && (end->u.integer + 1 < len)) {
      len = end->u.integer + 1;
    }
    if (len > 0 && start && (start->u.integer > 0)) {
      INC_PCHARP(buf, start->u.integer);
      len -= start->u.integer;
    }
    if (len < substring->len) {
      push_undefined();
      return;
    }
    if (!substring->len) {
      push_int(0);
      return;
    }
    if (substring->size_shift > THIS->str.s->size_shift) {
      push_undefined();
      return;
    }

    mojt = compile_memsearcher(MKPCHARP_STR(substring), substring->len,
			       len, substring);

    res = mojt.vtab->funcN(mojt.data, buf, len);

    if (!res.ptr) {
      push_undefined();
    } else {
      push_int64((((char *)res.ptr) - ((char *)buf.ptr)) >>
		 THIS->str.s->size_shift);
    }
  }

  }

#if defined(f_Buffer_cq__search_1_defined) || defined(f_Buffer_cq__search_2_defined)
#define f_Buffer_cq__search_defined
DEFAULT_CMOD_STORAGE void f_Buffer_cq__search(INT32 args) {
  if(args < 1) wrong_number_of_args_error("_search",args,1);
  switch(TYPEOF(Pike_sp[0-args])) {
   case PIKE_T_INT:

#ifdef f_Buffer_cq__search_1_defined
    f_Buffer_cq__search_1(args);
    return;

#endif /* f_Buffer_cq__search_1_defined */
    break;
   case PIKE_T_STRING:

#ifdef f_Buffer_cq__search_2_defined
    f_Buffer_cq__search_2(args);
    return;

#endif /* f_Buffer_cq__search_2_defined */
    break;
   default:
    SIMPLE_ARG_TYPE_ERROR("_search",1,"int|string");
  }
}

#endif /* f_Buffer_cq__search_1_defined, f_Buffer_cq__search_2_defined */

#undef internal_init_Buffer_defined
#define internal_init_Buffer_defined

#undef Buffer_event_handler_defined
#define Buffer_event_handler_defined
static void init_Buffer_struct(void)
#line 1891 "/Users/hww3/devel/pike/src/string_builder.cmod"
{
      struct Buffer_struct *str = THIS;
      memset( str, 0, sizeof( *str ) );
    }

  
#undef internal_exit_Buffer_defined
#define internal_exit_Buffer_defined

#undef Buffer_event_handler_defined
#define Buffer_event_handler_defined
static void exit_Buffer_struct(void)
#line 1898 "/Users/hww3/devel/pike/src/string_builder.cmod"
{
      struct Buffer_struct *str = THIS;
      if( str->str.s )
      {
          if( Pike_fp->flags & OBJECT_CLEAR_ON_EXIT )
            secure_zero( str->str.s->str, str->str.s->len );
          free_string_builder( &str->str );
      }
    }

  
#undef internal_gc_recurse_Buffer_defined
#define internal_gc_recurse_Buffer_defined

#undef Buffer_event_handler_defined
#define Buffer_event_handler_defined
static void gc_recurse_Buffer_struct(void)
#line 1909 "/Users/hww3/devel/pike/src/string_builder.cmod"
{
    if (mc_count_bytes (Pike_fp->current_object) && THIS->str.s)
      mc_counted_bytes += THIS->str.malloced;
  }

#ifdef Buffer_event_handler_defined
static void Buffer_event_handler(int ev) {
  switch(ev) {

#ifdef internal_init_Buffer_defined
  case PROG_EVENT_INIT: init_Buffer_struct(); break;

#endif /* internal_init_Buffer_defined */

#ifdef internal_exit_Buffer_defined
  case PROG_EVENT_EXIT: exit_Buffer_struct(); break;

#endif /* internal_exit_Buffer_defined */

#ifdef internal_gc_recurse_Buffer_defined
  case PROG_EVENT_GC_RECURSE: gc_recurse_Buffer_struct(); break;

#endif /* internal_gc_recurse_Buffer_defined */
  default: break; 
  }
}

#endif /* Buffer_event_handler_defined */

#ifdef THIS_

#undef THIS
#define THIS THIS_

#endif /* THIS_ */
/*! @endclass
 */

/*! @endmodule String
 */

#line 1921 "/Users/hww3/devel/pike/src/string_builder.cmod"
void init_string_buffer(void)
{
  
#ifdef CMOD_MAP_PROGRAM_IDS_DEFINED
set_program_id_to_id( ___cmod_map_program_ids );

#endif /* CMOD_MAP_PROGRAM_IDS_DEFINED */

#ifdef class_Buffer_defined

#ifdef PROG_BUFFER_ID
#line 1438 "/Users/hww3/devel/pike/src/string_builder.cmod"
  START_NEW_PROGRAM_ID(BUFFER);
#else
#line 1438 "/Users/hww3/devel/pike/src/string_builder.cmod"
  start_new_program();

#endif /* PROG_BUFFER_ID */
#line 1438 "/Users/hww3/devel/pike/src/string_builder.cmod"
  Buffer_program = Pike_compiler->new_program;

#ifndef tObjImpl_BUFFER

#undef tObjIs_BUFFER
#define tObjIs_BUFFER "\3\1\177\0\0\2"

#undef tObjImpl_BUFFER
#define tObjImpl_BUFFER "\3\0\177\0\0\2"

#endif /* tObjImpl_BUFFER */

#ifdef THIS_BUFFER
  Buffer_storage_offset = ADD_STORAGE(struct Buffer_struct);
#endif /* THIS_BUFFER */

#ifdef Buffer_event_handler_defined
  pike_set_prog_event_callback(Buffer_event_handler);

#ifndef Buffer_gc_live_obj
  Pike_compiler->new_program->flags &= ~PROGRAM_LIVE_OBJ;

#endif /* Buffer_gc_live_obj */

#endif /* Buffer_event_handler_defined */

#ifdef f_Buffer_cq__size_object_defined

#ifdef f_Buffer_cq__size_object_fun_num_used
  f_Buffer_cq__size_object_fun_num =
#endif /* f_Buffer_cq__size_object_fun_num_used */
#line 1443 "/Users/hww3/devel/pike/src/string_builder.cmod"
    ADD_FUNCTION2("_size_object", f_Buffer_cq__size_object, tFunc(tNone,"\10\200\0\0\0\177\377\377\377"), 0, OPT_EXTERNAL_DEPEND|OPT_SIDE_EFFECT);

#endif /* f_Buffer_cq__size_object_defined */

#ifdef f_Buffer_create_defined

#ifdef f_Buffer_create_fun_num_used
  f_Buffer_create_fun_num =
#endif /* f_Buffer_create_fun_num_used */
#line 1463 "/Users/hww3/devel/pike/src/string_builder.cmod"
    ADD_FUNCTION2("create", f_Buffer_create, tFunc(tOr("\10\200\0\0\0\177\377\377\377",tVoid),tVoid), 0, OPT_EXTERNAL_DEPEND|OPT_SIDE_EFFECT);

#endif /* f_Buffer_create_defined */

#ifdef f_Buffer_cq__sprintf_defined

#ifdef f_Buffer_cq__sprintf_fun_num_used
  f_Buffer_cq__sprintf_fun_num =
#endif /* f_Buffer_cq__sprintf_fun_num_used */
#line 1477 "/Users/hww3/devel/pike/src/string_builder.cmod"
    ADD_FUNCTION2("_sprintf", f_Buffer_cq__sprintf, tFunc("\10\200\0\0\0\177\377\377\377" tMapping,tStr), 0, OPT_EXTERNAL_DEPEND|OPT_SIDE_EFFECT);

#endif /* f_Buffer_cq__sprintf_defined */

#ifdef f_Buffer_cast_defined

#ifdef f_Buffer_cast_fun_num_used
  f_Buffer_cast_fun_num =
#endif /* f_Buffer_cast_fun_num_used */
#line 1524 "/Users/hww3/devel/pike/src/string_builder.cmod"
    ADD_FUNCTION2("cast", f_Buffer_cast, tFunc(tStr,tMix), ID_PROTECTED, OPT_EXTERNAL_DEPEND|OPT_SIDE_EFFECT);

#endif /* f_Buffer_cast_defined */

#ifdef f_Buffer_cq__backtick_add_defined

#ifdef f_Buffer_cq__backtick_add_fun_num_used
  f_Buffer_cq__backtick_add_fun_num =
#endif /* f_Buffer_cq__backtick_add_fun_num_used */
#line 1555 "/Users/hww3/devel/pike/src/string_builder.cmod"
    ADD_FUNCTION2("`+", f_Buffer_cq__backtick_add, tFunc(tOr(tString, tObjIs_BUFFER), tObjIs_BUFFER), 0, OPT_EXTERNAL_DEPEND|OPT_SIDE_EFFECT);

#endif /* f_Buffer_cq__backtick_add_defined */

#ifdef f_Buffer_cq__backtick_add_eq_defined

#ifdef f_Buffer_cq__backtick_add_eq_fun_num_used
  f_Buffer_cq__backtick_add_eq_fun_num =
#endif /* f_Buffer_cq__backtick_add_eq_fun_num_used */
#line 1572 "/Users/hww3/devel/pike/src/string_builder.cmod"
    ADD_FUNCTION2("`+=", f_Buffer_cq__backtick_add_eq, tFunc(tOr(tString, tObjIs_BUFFER), tObjIs_BUFFER), 0, OPT_EXTERNAL_DEPEND|OPT_SIDE_EFFECT);

#endif /* f_Buffer_cq__backtick_add_eq_defined */

#ifdef f_Buffer_add_defined

#ifdef f_Buffer_add_fun_num_used
  f_Buffer_add_fun_num =
#endif /* f_Buffer_add_fun_num_used */
#line 1605 "/Users/hww3/devel/pike/src/string_builder.cmod"
    ADD_FUNCTION2("add", f_Buffer_add, tFuncV(tNone, tOr(tString, tObjIs_BUFFER), tIntPos), 0, OPT_EXTERNAL_DEPEND|OPT_SIDE_EFFECT);

#endif /* f_Buffer_add_defined */

#ifdef f_Buffer_putchar_defined

#ifdef f_Buffer_putchar_fun_num_used
  f_Buffer_putchar_fun_num =
#endif /* f_Buffer_putchar_fun_num_used */
#line 1664 "/Users/hww3/devel/pike/src/string_builder.cmod"
    ADD_FUNCTION2("putchar", f_Buffer_putchar, tFunc("\10\200\0\0\0\177\377\377\377",tVoid), 0, OPT_EXTERNAL_DEPEND|OPT_SIDE_EFFECT);

#endif /* f_Buffer_putchar_defined */

#ifdef f_Buffer_sprintf_defined

#ifdef f_Buffer_sprintf_fun_num_used
  f_Buffer_sprintf_fun_num =
#endif /* f_Buffer_sprintf_fun_num_used */
#line 1675 "/Users/hww3/devel/pike/src/string_builder.cmod"
    ADD_FUNCTION2("sprintf", f_Buffer_sprintf, tFuncV(tAttr("strict_sprintf_format", tOr(tStr, tObj)),
		   tAttr("sprintf_args", tMix), tStr), 0, OPT_EXTERNAL_DEPEND|OPT_SIDE_EFFECT);

#endif /* f_Buffer_sprintf_defined */

#ifdef f_Buffer_get_copy_defined

#ifdef f_Buffer_get_copy_fun_num_used
  f_Buffer_get_copy_fun_num =
#endif /* f_Buffer_get_copy_fun_num_used */
#line 1696 "/Users/hww3/devel/pike/src/string_builder.cmod"
    ADD_FUNCTION2("get_copy", f_Buffer_get_copy, tFunc(tNone,tStr), 0, OPT_EXTERNAL_DEPEND|OPT_SIDE_EFFECT);

#endif /* f_Buffer_get_copy_defined */

#ifdef f_Buffer_get_defined

#ifdef f_Buffer_get_fun_num_used
  f_Buffer_get_fun_num =
#endif /* f_Buffer_get_fun_num_used */
#line 1733 "/Users/hww3/devel/pike/src/string_builder.cmod"
    ADD_FUNCTION2("get", f_Buffer_get, tFunc(tNone,tStr), 0, OPT_EXTERNAL_DEPEND|OPT_SIDE_EFFECT);

#endif /* f_Buffer_get_defined */

#ifdef f_Buffer_clear_defined

#ifdef f_Buffer_clear_fun_num_used
  f_Buffer_clear_fun_num =
#endif /* f_Buffer_clear_fun_num_used */
#line 1760 "/Users/hww3/devel/pike/src/string_builder.cmod"
    ADD_FUNCTION2("clear", f_Buffer_clear, tFunc(tNone,tVoid), 0, OPT_EXTERNAL_DEPEND|OPT_SIDE_EFFECT);

#endif /* f_Buffer_clear_defined */

#ifdef f_Buffer_cq__sizeof_defined

#ifdef f_Buffer_cq__sizeof_fun_num_used
  f_Buffer_cq__sizeof_fun_num =
#endif /* f_Buffer_cq__sizeof_fun_num_used */
#line 1777 "/Users/hww3/devel/pike/src/string_builder.cmod"
    ADD_FUNCTION2("_sizeof", f_Buffer_cq__sizeof, tFunc(tNone,"\10\200\0\0\0\177\377\377\377"), ID_PROTECTED, OPT_EXTERNAL_DEPEND|OPT_SIDE_EFFECT);

#endif /* f_Buffer_cq__sizeof_defined */

#ifdef f_Buffer_cq__search_defined

#ifdef Buffer_f_Buffer_cq__search_fun_num_used
  Buffer_f_Buffer_cq__search_fun_num =
#endif /* Buffer_f_Buffer_cq__search_fun_num_used */
#line 1849 "/Users/hww3/devel/pike/src/string_builder.cmod"
    ADD_FUNCTION2("_search", f_Buffer_cq__search, tOr(tFunc("\10\200\0\0\0\177\377\377\377" tOr("\10\200\0\0\0\177\377\377\377",tVoid) tOr("\10\200\0\0\0\177\377\377\377",tVoid),"\10\0\0\0\0\177\377\377\377"),tFunc(tStr tOr("\10\200\0\0\0\177\377\377\377",tVoid) tOr("\10\200\0\0\0\177\377\377\377",tVoid),"\10\0\0\0\0\177\377\377\377")), 0, OPT_EXTERNAL_DEPEND|OPT_SIDE_EFFECT);

#endif /* f_Buffer_cq__search_defined */
#line 1438 "/Users/hww3/devel/pike/src/string_builder.cmod"
  Buffer_program=end_program();
#line 1438 "/Users/hww3/devel/pike/src/string_builder.cmod"
#ifdef Buffer_program_fun_num_used
Buffer_program_fun_num=
#endif
 add_program_constant("Buffer",Buffer_program,0);

#endif /* class_Buffer_defined */

#ifdef CMOD_MAP_PROGRAM_IDS_DEFINED
set_program_id_to_id( 0 );
#endif /* CMOD_MAP_PROGRAM_IDS_DEFINED */
#line 1923 "/Users/hww3/devel/pike/src/string_builder.cmod"
;
}

void exit_string_buffer(void)
{
  
#ifdef class_Buffer_defined
  if(Buffer_program) {
#line 1438 "/Users/hww3/devel/pike/src/string_builder.cmod"
    free_program(Buffer_program);
    Buffer_program=0;
  }

#endif /* class_Buffer_defined */
#line 1928 "/Users/hww3/devel/pike/src/string_builder.cmod"
;
}


#ifdef CMOD_MAP_PROGRAM_IDS_DEFINED
static int ___cmod_map_program_ids(int id)
{
  if( (id&0x7f000000) != 0x7f000000 ) return id;
  id = id&0x00ffffff;
  switch(id) {
#ifdef class_Buffer_defined
  case 2:
    return Buffer_program->id;
    break;
#endif
  };

  return 0;
}
#endif /* CMOD_MAP_PROGRAM_IDS_DEFINED */
