Skip to content
Snippets Groups Projects
Select Git revision
  • f33cdba8ad2d2d9ad176e3abe8080b1bf76d1b88
  • master default
  • dbck-q-n-d-link
  • foutput-text_stat-override
  • generations
  • text-stat-sha256
  • use-nettle
  • import-nettle
  • refactor-cached_get_text
  • refactor-cached_get_text-part-2
  • add-text_store
  • introduce-generation_position
  • remove-reclamation
  • dbfile-temp-filenames
  • sstrdup
  • dbfile_open_read-check-magic
  • adns_dist
  • liboop_dist
  • search
  • isc
  • dbdbckmultiplechoice
  • last.cvs.revision
  • 2.1.2
  • 2.1.1
  • 2.1.0
  • adns_1_0
  • liboop_0_9
  • 2.0.7
  • search_bp
  • 2.0.6
  • 2.0.5
  • isc_1_01
  • Protocol-A-10.4
  • 2.0.4
  • 2.0.3
  • 2.0.2
  • 2.0.1
  • 2.0.0
  • isc_1_00
  • isc_merge_1999_05_01
  • isc_merge_1999_04_21
41 results

checkargs.py

Blame
  • builtin.cmod 40.81 KiB
    /* -*- c -*-
     * $Id: builtin.cmod,v 1.59 2001/07/13 11:26:37 grubba Exp $
     */
    
    #include "global.h"
    #include "interpret.h"
    #include "svalue.h"
    #include "opcodes.h"
    #include "pike_macros.h"
    #include "object.h"
    #include "program.h"
    #include "array.h"
    #include "pike_error.h"
    #include "constants.h"
    #include "mapping.h"
    #include "stralloc.h"
    #include "multiset.h"
    #include "pike_types.h"
    #include "pike_memory.h"
    #include "threads.h"
    #include <math.h>
    #include <ctype.h>
    #include "module_support.h"
    #include "cyclic.h"
    #include "bignum.h"
    #include "main.h"
    #include "operators.h"
    #include "builtin_functions.h"
    #include "fsort.h"
    
    
    /*! @decl string basetype(mixed x)
     *!
     *! Same as sprintf("%t",x);
     *!
     *! @seealso
     *! @[sprintf()]
     */
    PIKEFUN string basetype(mixed x)
      efun;
      optflags OPT_TRY_OPTIMIZE;
    {
      int t=x->type;
      if(x->type == T_OBJECT && x->u.object->prog)
      {
        ptrdiff_t fun=FIND_LFUN(x->u.object->prog, LFUN__SPRINTF);
        if(fun != -1)
        {
          push_int('t');
          f_aggregate_mapping(0);
          apply_low(x->u.object, fun, 2);
          if(Pike_sp[-1].type == T_STRING)
          {
    	stack_swap();
    	pop_stack();
    	return;
          } else if (IS_ZERO(Pike_sp-1)) {
    	pop_n_elems(2);
    	push_constant_text("object");
    	return;
          } else {
    	Pike_error("Non-string returned from _sprintf()\n");
          }
        }
      }
      pop_stack();
      switch(t)
      {
        case T_ARRAY: push_constant_text("array"); break;
        case T_FLOAT: push_constant_text("float"); break;
        case T_FUNCTION: push_constant_text("function"); break;
        case T_INT: push_constant_text("int"); break;
        case T_LVALUE: push_constant_text("lvalue"); break;
        case T_MAPPING: push_constant_text("mapping"); break;
        case T_MULTISET: push_constant_text("multiset"); break;
        case T_OBJECT: push_constant_text("object"); break;
        case T_PROGRAM: push_constant_text("program"); break;
        case T_STRING: push_constant_text("string"); break;
        case T_TYPE: push_constant_text("type"); break;
        case T_ZERO: push_constant_text("zero"); break;
        case T_VOID: push_constant_text("void"); break;
        case T_MAPPING_DATA: push_constant_text("mapping_data"); break;
        default: push_constant_text("unknown"); break;
      }
    }
    
    
    /*! @decl string int2char(int x)
     *!
     *! Same as sprintf("%c",x);
     *!
     *! @seealso
     *! @[sprintf()]
     */
    PIKEFUN string int2char(int|object x)
      efun;
      optflags OPT_TRY_OPTIMIZE;
    {
      int c;
      if(x->type == T_OBJECT && x->u.object->prog)
      {
        ptrdiff_t fun=FIND_LFUN(x->u.object->prog, LFUN__SPRINTF);
        if(fun != -1)
        {
          push_int('c');
          f_aggregate_mapping(0);
          apply_low(x->u.object, fun, 2);
          if(Pike_sp[-1].type == T_STRING)
          {
    	stack_swap();
    	pop_stack();
    	return;
          }
          Pike_error("Non-string returned from _sprintf()\n");
        }
      }
      if(x->type != T_INT)
        Pike_error("Bad argument 1 to int2char.\n");
    
      c=x->u.integer;
    
      if(c>=0 && c<256)
      {
        struct pike_string *s;
        s=begin_shared_string(1);
        s->str[0]=c;
        RETURN end_shared_string(s);
      }else{
        struct string_builder tmp;
        init_string_builder(&tmp,0);
        string_builder_putchar(&tmp, c);
        RETURN finish_string_builder(&tmp);
      }
    }
    
    /*! @decl array column(array data, mixed index)
     *!
     *! Extract a column from a two-dimensional array.
     *!
     *! This function is exactly equivalent to:
     *! @code{map(@[data], lambda(mixed x,mixed y) { return x[y]; }, @[index])@}
     *!
     *! Except of course it is a lot shorter and faster.
     *! That is, it indices every index in the array data on the value of
     *! the argument index and returns an array with the results.
     *!
     *! @seealso
     *! @[rows()]
     */
    PIKEFUN array column(array data, mixed index)
      efun;
      optflags OPT_TRY_OPTIMIZE;
    {
      INT32 e;
      struct array *a;
    
      DECLARE_CYCLIC();
    
      /* Optimization */
      if(data->refs == 1)
      {
        /* An array with one ref cannot possibly be cyclic */
        struct svalue sval;
        data->type_field = BIT_MIXED | BIT_UNFINISHED;
        for(e=0;e<data->size;e++)
        {
          index_no_free(&sval, ITEM(data)+e, index);
          free_svalue(ITEM(data)+e);
          ITEM(data)[e]=sval;
        }
        pop_stack();
        return;
      }
    
      if((a=(struct array *)BEGIN_CYCLIC(data,0)))
      {
        add_ref(a);
      }else{
        push_array(a=allocate_array(data->size));
        SET_CYCLIC_RET(a);
    
        for(e=0;e<a->size;e++)
          index_no_free(ITEM(a)+e, ITEM(data)+e, index);
    
        sp--;
      }
      END_CYCLIC();
      RETURN a;
    }
    
    /*! @decl multiset mkmultiset(array a)
     *!
     *! This function creates a multiset from an array.
     *!
     *! @seealso
     *! @[aggregate_multiset()]
     *!
     */
    PIKEFUN multiset(1) mkmultiset(array(1=mixed) a)
      efun;
      optflags OPT_TRY_OPTIMIZE|OPT_EXTERNAL_DEPEND;
    {
      RETURN mkmultiset(a);
    }
    
    /*! @decl int trace(int t)
     *!
     *! This function changes the debug trace level.
     *!
     *! The old level is returned.
     *!
     *! Trace level 1 or higher means that calls to Pike functions are
     *! printed to stderr, level 2 or higher means calls to builtin functions
     *! are printed, 3 means every opcode interpreted is printed, 4 means
     *! arguments to these opcodes are printed as well.
     *!
     *! See the @tt{-t@} command-line option for more information.
     */
    PIKEFUN int trace(int t)
      efun;
      optflags OPT_SIDE_EFFECT;
    {
      pop_n_elems(args);
      push_int(t_flag);
      t_flag=t;
    }
    
    /*! @decl string ctime(int timestamp)
     *!
     *! Convert the output from a previous call to @[time()] into a readable
     *! string containing the current year, month, day and time.
     *!
     *! @seealso
     *! @[time()], @[localtime()], @[mktime()], @[gmtime()]
     */
    PIKEFUN string ctime(int timestamp)
      efun;
      optflags OPT_TRY_OPTIMIZE;
    {
      time_t i=(time_t)timestamp;
      RETURN make_shared_string(ctime(&i));
    }
    
    /*! @decl mapping mkmapping(array ind, array val)
     *!
     *! Make a mapping from two arrays.
     *!
     *! Makes a mapping @[ind[x]]:@[val[x]], @tt{0 <= x < sizeof(ind)@}.
     *!
     *! @[ind] and @[val] must have the same size.
     *!
     *! This is the inverse operation of @[indices()] and @[values()].
     *!
     *! @seealso
     *! @[indices()], @[values()]
     */
    PIKEFUN mapping(1:2) mkmapping(array(1=mixed) ind, array(2=mixed) val)
      efun;
      optflags OPT_TRY_OPTIMIZE|OPT_EXTERNAL_DEPEND;
    {
      if(ind->size != val->size)
        bad_arg_error("mkmapping", sp-args, args, 2, "array", sp+1-args,
    		  "mkmapping called on arrays of different sizes (%d != %d)\n",
    		  ind->size, val->size);
    
      RETURN mkmapping(ind, val);
    }
    
    /*! @decl int String.count(string haystack, string needle)
     *!
     *! Count the number of non-overlapping times the string @[needle] occurrs
     *! in the string @[haystack].
     *!
     *! @seealso
     *! @[search()], @[`/()]
     */
    PIKEFUN int string_count(string haystack, string needle)
      errname String.count;
      optflags OPT_TRY_OPTIMIZE;
    {
       ptrdiff_t c = 0;
       ptrdiff_t i, j;
    
       switch (needle->len)
       {
         case 0:
           switch (haystack->len)
           {
    	 case 0: c=1; break; /* "" appears one time in "" */
    	 case 1: c=0; break; /* "" doesn't appear in "x" */
    	 default: c=haystack->len-1; /* one time between each character */
           }
           break;
         case 1:
           /* maybe optimize? */
         default:
           for (i=0; i<haystack->len; i++)
           {
    	 j=string_search(haystack,needle,i);
    	 if (j==-1) break;
    	 i=j+needle->len-1;
    	 c++;
           }
           break;
       }
       RETURN DO_NOT_WARN((INT_TYPE)c);
    }
    
    /*! @decl string String.trim_whites (string s)
     *!
     *! Trim leading and trailing spaces and tabs from the string @[s].
     */
    PIKEFUN string string_trim_whites (string s)
      errname String.trim_whites;
      optflags OPT_TRY_OPTIMIZE;
    {
      ptrdiff_t start = 0, end = s->len;
      int chr;
      switch (s->size_shift) {
    #define DO_IT(TYPE)							\
        {									\
          for (; start < s->len; start++) {					\
    	chr = ((TYPE *) s->str)[start];					\
    	if (chr != ' ' && chr != '\t') break;				\
          }									\
          while (--end > start) {						\
    	chr = ((TYPE *) s->str)[end];					\
    	if (chr != ' ' && chr != '\t') break;				\
          }									\
        }
        case 0: DO_IT (p_wchar0); break;
        case 1: DO_IT (p_wchar1); break;
        case 2: DO_IT (p_wchar2); break;
    #undef DO_IT
      }
      RETURN string_slice (s, start, end + 1 - start);
    }
    
    /*! @decl string String.trim_all_whites (string s)
     *!
     *! Trim leading and trailing white spaces characters (space, tab,
     *! newline and carriage return) from the string @[s].
     */
    PIKEFUN string string_trim_all_whites (string s)
      errname String.trim_all_whites;
      optflags OPT_TRY_OPTIMIZE;
    {
      ptrdiff_t start = 0, end = s->len;
      int chr;
      switch (s->size_shift) {
    #define DO_IT(TYPE)							\
        {									\
          for (; start < s->len; start++) {					\
    	chr = ((TYPE *) s->str)[start];					\
    	if (chr != ' ' && chr != '\t' && chr != '\n' && chr != '\r')	\
    	  break;							\
          }									\
          while (--end > start) {						\
    	chr = ((TYPE *) s->str)[end];					\
    	if (chr != ' ' && chr != '\t' && chr != '\n' && chr != '\r')	\
    	  break;							\
          }									\
        }
        case 0: DO_IT (p_wchar0); break;
        case 1: DO_IT (p_wchar1); break;
        case 2: DO_IT (p_wchar2); break;
    #undef DO_IT
      }
      RETURN string_slice (s, start, end + 1 - start);
    }
    
    /*! @decl int Program.implements(program prog, program api)
     *!
     *! Returns 1 if @[prog] implements @[api].
     */
    PIKEFUN int program_implements(program prog, program api)
      errname Program.implements;
      optflags OPT_TRY_OPTIMIZE;
    {
      RETURN implements(prog, api);
    }
    
    /*! @decl int Program.inherits(program child, program parent)
     *!
     *! Returns 1 if @[child] has inherited @[parent].
     */
    PIKEFUN int program_inherits(program parent, program child)
      errname Program.inherits;
      optflags OPT_TRY_OPTIMIZE;
    {
      RETURN low_get_storage(parent, child) != -1;
    }
    
    /*! @decl string Program.defined(program p)
     *!
     *! Returns a string with filename and linenumber describing where
     *! the program @[p] was defined.
     *!
     *! The returned string is of the format @tt{"@i{filename@}:@i{linenumber@}"@}.
     *!
     *! If it cannot be determined where the program was defined, @tt{0@} (zero)
     *! will be returned.
     */
    PIKEFUN string program_defined(program p)
      errname Program.defined;
      optflags OPT_TRY_OPTIMIZE;
    {
      if(p && p->num_linenumbers)
      {
        INT32 line;
        struct pike_string *tmp = get_program_line(p, &line);
    
        pop_n_elems(args);
    
        push_string(tmp);
        if(line >= 1)
        {
          push_constant_text(":");
          push_int(line);
          f_add(3);
        }
        return;
      }
    
      pop_n_elems(args);
      push_int(0);
    }
    
    /*! @decl int(8..8)|int(16..16)|int(32..32) String.width(string s)
     *!
     *! Returns the width of a string.
     *!
     *! Three return values are possible:
     *! @int
     *!   @value 8
     *!     The string @[s] only contains characters <= 255.
     *!   @value 16
     *!     The string @[s] only contains characters <= 65535.
     *!   @value 32
     *!     The string @[s] contains characters >= 65536.
     *! @endint
     */
    PIKEFUN int(8 .. 8)|int(16 .. 16)|int(32 .. 32) string_width(string s)
      errname String.width;
      optflags OPT_TRY_OPTIMIZE;
    {
      RETURN 8 * (1 << s->size_shift);
    }
    
    /*! @decl mixed m_delete(object|mapping map, mixed index)
     *!
     *! If @[map] is an object that implements @[lfun::_m_delete()],
     *! that function will be called with @[index] as the signle argument.
     *!
     *! Other wise if @[map] is a mapping the entry with index @[index]
     *! will be removed from @[map] destructively.
     *!
     *! If the mapping does not have an entry with index @[index], nothing is done.
     *!
     *! @returns
     *!   The value that was removed will be returned.
     *!
     *! @note
     *!   Note that @[m_delete()] changes @[map] destructively.
     *!
     *! @seealso
     *!   @[mappingp()]
     */
    PIKEFUN mixed m_delete(object|mapping map, mixed index)
      efun;
      optflags OPT_SIDE_EFFECT;
    {
      /*FIXME: Should be
       *     type function(mapping(1=mixed:2=mixed),1:2)|
       *     function(object,mixed:mixed);
       *
       *     or similar
       */
      if( map->type == T_MAPPING )
      {
        struct svalue s;
        map_delete_no_free(map->u.mapping, index, &s);
        pop_n_elems(args);
        *sp=s;
        sp++;
      }
      else if (map->type == T_OBJECT && map->u.object->prog)
      {
        int id = FIND_LFUN(map->u.object->prog, LFUN__M_DELETE);
    
        if( id == -1 )
          SIMPLE_BAD_ARG_ERROR("m_delete", 1, "object with _m_delete");
        
        apply_low( map->u.object, id, 1 );
        stack_swap();
        pop_stack();
      } else {
        SIMPLE_BAD_ARG_ERROR("m_delete", 1, "object|mapping");
      }
    }
    
    /*! @decl int get_weak_flag(array|mapping|multiset m)
     *!
     *! Returns the weak flag settings for @[m]. It's a combination of
     *! @[Pike.WEAK_INDICES] and @[Pike.WEAK_VALUES].
     */
    PIKEFUN int get_weak_flag(array m)
      efun;
      optflags OPT_EXTERNAL_DEPEND;
    {
      RETURN (m->flags & ARRAY_WEAK_FLAG) ? PIKE_WEAK_VALUES : 0;
    }
    
    PIKEFUN int get_weak_flag(mapping m)
    {
      RETURN  mapping_get_flags(m) & MAPPING_WEAK;
    }
    
    PIKEFUN int get_weak_flag(multiset m)
    {
      RETURN (m->ind->flags & (ARRAY_WEAK_FLAG|ARRAY_WEAK_SHRINK)) ?
    	PIKE_WEAK_INDICES : 0;
    }
    
    PIKEFUN program __empty_program()
      efun;
      optflags OPT_EXTERNAL_DEPEND;
    {
      RETURN low_allocate_program();
    }
    
    /*! @decl string function_name(function f)
     *!
     *! Return the name of the function @[f].
     *!
     *! If @[f] is a global function defined in the runtime @tt{0@} (zero)
     *! will be returned.
     *!
     *! @seealso
     *! @[function_object()]
     */
    PIKEFUN string function_name(program|function func)
      efun;
      optflags OPT_TRY_OPTIMIZE;
    {
      struct pike_string *s;
      switch(func->type)
      {
        default:
          if(!func->u.object->prog)
    	bad_arg_error("function_name", Pike_sp-args, args, 1,
    		      "function|program", Pike_sp-args,
    		      "Bad argument.\n");
          return; /* NOTREACHED */
    	
        case PIKE_T_PROGRAM:
        {
          struct program *p=func->u.program;
    
          if(p->parent)
          {
    	int e;
    	p=p->parent;
    	/* search constants in parent for this
    	 * program...
    	 */
    	
    	for(e = p->num_identifier_references; e--; )
    	{
    	  struct identifier *id;
    	  if (p->identifier_references[e].id_flags & ID_HIDDEN)
    	    continue;
    
    	  id = ID_FROM_INT(p, e);
    	  if (IDENTIFIER_IS_CONSTANT(id->identifier_flags) &&
    	      is_eq( & PROG_FROM_INT(p, e)->constants[id->func.offset].sval,
    		     func))
    	    REF_RETURN id->name;
    	}
          }
          break;
        }
    
        case PIKE_T_FUNCTION:
          if(func->subtype == FUNCTION_BUILTIN) break;
          if(!func->u.object->prog)
    	bad_arg_error("function_name", Pike_sp-args, args, 1,
    		      "function", Pike_sp-args,
    		      "Destructed object.\n");
          if(func->u.object->prog == pike_trampoline_program)
          {
    	struct pike_trampoline *t;
    	t=((struct pike_trampoline *)func->u.object->storage);
    	if(t->frame->current_object->prog)
    	  REF_RETURN ID_FROM_INT(t->frame->current_object->prog,
    				 t->func)->name;
          }
          
          REF_RETURN ID_FROM_INT(func->u.object->prog, func->subtype)->name;
      }
      pop_n_elems(args);
      push_int(0);
    }
    
    /*! @decl object function_object(function|program f)
     *!
     *! Return the object the function @[f] is in.
     *!
     *! If @[f] is a global function defined in the runtime @tt{0@} (zero)
     *! will be returned.
     *!
     *! @seealso
     *! @[function_name()]
     */
    PIKEFUN object|program function_object(object|program|function func)
      efun;
      optflags OPT_TRY_OPTIMIZE;
      type function(function|object:object)|function(program:program);
    {
      switch(func->type)
      {
        case PIKE_T_PROGRAM:
        {
          struct program *p;
          if(!(p=func->u.program->parent)) break;
          add_ref(p);
          free_program(func->u.program);
          func->u.program=p;
          return;
        }
          
        case PIKE_T_FUNCTION:
          if(func->subtype == FUNCTION_BUILTIN) break;
          if(func->u.object->prog == pike_trampoline_program)
          {
    	struct object *o;
    	o=((struct pike_trampoline *)func->u.object->storage)->frame->current_object;
    	add_ref(o);
    	pop_n_elems(args);
    	push_object(o);
    	return;
          }
          func->type=T_OBJECT;
          return;
    
    
        default:
          SIMPLE_BAD_ARG_ERROR("function_object",1,"function");
      }
      pop_n_elems(args);
      push_int(0);
    }
    
    
    
    /*! @decl int random(int max)
     *!
     *!   This function returns a random number in the range 0 - @[max]-1.
     *!
     *! @seealso
     *!   @[random_seed()]
     */
    
    
    PIKEFUN mixed random(object o)
      efun;
      optflags OPT_TRY_OPTIMIZE|OPT_EXTERNAL_DEPEND;
    {
      apply(o,"_random",0);
      stack_swap();
      pop_stack();
    }
    
    PIKEFUN int random(int i)
    {
      if(i <= 0) RETURN 0;
      RETURN  my_rand() % i;
    }      
    
    PIKEFUN float random(float f)
    {
      if(f<=0.0) RETURN 0.0;
    #define N 1048576
      RETURN f * (my_rand()%N/((float)N)) +
        f * (my_rand()%N/( ((float)N) * ((float)N) ));
    		   
    }
    
    PIKEFUN mixed random(array a)
    {
      if(!a->size)
        SIMPLE_BAD_ARG_ERROR("random", 1, "array with elements in it");
      push_svalue(a->item + (my_rand() % a->size));
      stack_swap();
      pop_stack();
    }
    
    PIKEFUN mixed random(multiset m)
    {
      if(!m->ind->size)
        SIMPLE_BAD_ARG_ERROR("random", 1, "multiset with elements in it");
      push_svalue(m->ind->item + (my_rand() % m->ind->size));
      stack_swap();
      pop_stack();
    }
    
    PIKEFUN mapping random(mapping m)
    {
      struct mapping_data *md=m->data;
      size_t bucket, count;
      struct keypair *k;
      
      if(!m_sizeof(m))
        SIMPLE_BAD_ARG_ERROR("random", 1, "mapping with elements in it");
      
      /* Find a random, nonempty bucket */
      bucket=my_rand() % md->hashsize;
      while(! md->hash[bucket] )
        if(++bucket > (size_t)md->hashsize)
          bucket=0;
      
      /* Count entries in bucket */
      count=0;
      for(k=md->hash[bucket];k;k=k->next) count++;
      
      /* Select a random entry in this bucket */
      count = my_rand() % count;
      k=md->hash[bucket];
      while(count-- > 0) k=k->next;
      
      /* Push result and return */
      push_svalue(&k->ind);
      push_svalue(&k->val);
      f_aggregate(2);
      stack_swap();
      pop_stack();
    }
    
    /*
     * Backtrace handling.
     */
    
    /*! @class BacktraceFrame
     */
    
    PIKECLASS backtrace_frame
    {
      PIKEVAR mixed fun;
      PIKEVAR array args;
      CVAR struct program *prog;	/* FIXME: Ought to be a private pikevar... */
      CVAR unsigned char *pc;
      CVAR struct pike_string *filename;
      CVAR INT_TYPE lineno;
    
      INIT
      {
        THIS->fun.type = T_INT;
        THIS->fun.u.integer = 0;
        THIS->prog = NULL;
        THIS->pc = 0;
        THIS->lineno = 0;
        THIS->args = NULL;
        THIS->filename = NULL;
      }
    
      EXIT
      {
        if (THIS->prog) {
          free_program(THIS->prog);
          THIS->prog = NULL;
        }
        if (THIS->args) {
          free_array(THIS->args);
          THIS->args = NULL;
        }
        if (THIS->filename) {
          free_string(THIS->filename);
          THIS->filename = NULL;
        }
        THIS->pc = NULL;
        THIS->lineno = 0;
        free_svalue(&THIS->fun);
        THIS->fun.type = T_INT;
        THIS->fun.u.integer = 0;
      }
    
      PIKEFUN int(0..1) _is_type(string t)
      {
        INT_TYPE res = (t == findstring("array"));
        pop_n_elems(args);
        push_int(res);
      }
    
      PIKEFUN string _sprintf(int c, mapping|void opts)
      {
        pop_n_elems(args);
    
        push_text("backtrace_frame(");
        if (THIS->pc) {
          if (!THIS->filename) {
    	THIS->filename = get_line(THIS->pc, THIS->prog, &THIS->lineno);
          }
          THIS->pc = NULL;
        }
        if (THIS->prog) {
          free_program(THIS->prog);
          THIS->prog = NULL;
        }
        if (THIS->filename) {
          ref_push_string(THIS->filename);
          push_text(":");
          push_int(THIS->lineno);
          push_text(", ");
          f_add(4);
        } else {
          push_text("Unknown file, ");
        }
        if (THIS->fun.type == PIKE_T_FUNCTION) {
          if (THIS->fun.u.object->prog) {
    	push_svalue(&THIS->fun);
    	f_function_name(1);
    	push_text("(), ");
    	f_add(2);
          } else {
    	free_svalue(&THIS->fun);
    	THIS->fun.type = PIKE_T_INT;
    	THIS->fun.u.integer = 0;
    	THIS->fun.subtype = NUMBER_DESTRUCTED;
    	push_text("destructed_function(), ");	
          }
        } else {
          push_text("destructed_function(), ");
        }
    
        if (THIS->args) {
          push_text("Args: ");
          push_int(THIS->args->size);
          f_add(2);
        } else {
          push_text("No args");
        }
        push_text(")");
        f_add(5);
      }
    
      PIKEFUN int _sizeof()
      {
        if (THIS->args) {
          push_int(THIS->args->size + 3);
        } else {
          push_int(3);
        }
      }
    
      PIKEFUN mixed `[](int index, int|void end_or_none)
      {
        INT_TYPE end = index;
        INT32 numargs = 0;
        INT32 i;
    
        if (THIS->args) {
          numargs = THIS->args->size;
        }
    
        numargs += 3;
    
        if (!end_or_none) {
          if (index < 0) {
    	index_error("pike_frame->`[]", Pike_sp-args, args, NULL, Pike_sp-args,
    		    "Indexing with negative index (%"PRINTPIKEINT"d)\n", index);
          } else if (index >= numargs) {
    	index_error("pike_frame->`[]", Pike_sp-args, args, NULL, Pike_sp-args,
    		    "Indexing with too large index (%"PRINTPIKEINT"d)\n", index);
          }
        } else {
          if (end_or_none->type != PIKE_T_INT) {
    	SIMPLE_BAD_ARG_ERROR("`[]",2,"int|void");
          }
          end = end_or_none->u.integer;
        }
    
        pop_n_elems(args);
    
        if (end_or_none) {
          if ((end < 0) || (end < index) || (index >= numargs)) {
    	f_aggregate(0);
    	return;
          }
    
          if (end >= numargs) {
    	end = numargs-1;
          }
        }
    
        for (i = index; i <= end; i++) {
          switch(i) {
          case 0:	/* Filename */
          case 1:	/* Linenumber */
    	if (THIS->pc) {
    	  if (!THIS->filename) {
    	    THIS->filename = get_line(THIS->pc, THIS->prog, &THIS->lineno);
    	  }
    	  THIS->pc = NULL;
    	}
            if (THIS->prog) {
    	  free_program(THIS->prog);
    	  THIS->prog = NULL;
    	}
            if (i) {
    	  /* Linenumber */
    	  push_int(THIS->lineno);
    	} else {
    	  /* Filename */
    	  if (THIS->filename) {
    	    ref_push_string(THIS->filename);
    	  } else {
    	    push_int(0);
    	  }
    	}
    	break;
          case 2:	/* Function */
            push_svalue(&THIS->fun);
            break;
          default:	/* Arguments */
            {
    	  if ((i > 2) && (THIS->args) && (i-3 < THIS->args->size)) {
    	    push_svalue(THIS->args->item + (i - 3));
    	    break;
    	  }
    	  bad_arg_error("backtrace_frame->`[]", Pike_sp-args, args, 1,
    			"int(0..)", Pike_sp-args,
    			"Bad argument 1 to backtrace_frame->`[](): "
    			"Expected int(0..%d)\n",
    			numargs + 2);
    	}
            /* NOT_REACHED */
            break;
          }
        }
        if (end_or_none) {
          f_aggregate(1 + end - index);
        }
      }
    
      PIKEFUN mixed `[]=(int index, mixed value)
      {
        INT32 numargs = 0;
        INT32 i;
    
        if (THIS->args) {
          numargs = THIS->args->size;
        }
    
        numargs += 3;
    
        if ((index < -numargs) || (index >= numargs)) {
    	index_error("pike_frame->`[]=", Pike_sp-args, args, NULL, Pike_sp-args,
    		    "Index %"PRINTPIKEINT"d is out of array range 0 - %d,\n",
    		    index, numargs-1);
        } else if (index < 0) {
          index += numargs;
        }
    
        if (args > 2) {
          pop_n_elems(args - 2);
          args = 2;
        }
    
        switch(index) {
        case 0:	/* Filename */
        case 1:	/* Linenumber */
          /* First make sure we have line-number info. */
          if (THIS->pc) {
    	if (!THIS->filename) {
    	  THIS->filename = get_line(THIS->pc, THIS->prog, &THIS->lineno);
    	}
    	THIS->pc = NULL;
          }
          if (THIS->prog) {
    	free_program(THIS->prog);
    	THIS->prog = NULL;
          }
          if (index) {
    	/* Linenumber */
    	if (value->type != PIKE_T_INT) {
    	  SIMPLE_BAD_ARG_ERROR("backtrace_frame->`[]=", 2, "int(1..)");
    	}
    	THIS->lineno = value->u.integer;
          } else {
    	/* Filename */
    	if (value->type != PIKE_T_STRING) {
    	  if ((value->type != PIKE_T_INT) ||
    	      (value->u.integer)) {
    	    SIMPLE_BAD_ARG_ERROR("backtrace_frame->`[]=", 2,
    				 "string|int(0..0)");
    	  }
    	  if (THIS->filename) {
    	    free_string(THIS->filename);
    	    THIS->filename = NULL;
    	  }
    	} else {
    	  if (THIS->filename) {
    	    free_string(THIS->filename);
    	    THIS->filename = NULL;
    	  }
    	  copy_shared_string(THIS->filename, value->u.string);
    	}
          }
          break;
        case 2:	/* Function */
          assign_svalue(&THIS->fun, value);
          break;
        default:	/* Arguments */
          assign_svalue(THIS->args->item + index - 3, value);
          break;
        }
        stack_swap();
        pop_stack();
      }
    
    };
    
    /*! @endclass
     */
    
    /*! @decl array(array) backtrace()
     *!
     *!   Get a description of the current call stack.
     *!
     *!   The description is returned as an array with one entry for each call
     *!   frame on the stack.
     *!
     *!   Each entry has this format:
     *!   @array
     *!     @elem string file
     *!       A string with the filename if known, else zero.
     *!     @elem int line
     *!       An integer containing the linenumber if known, else zero.
     *!     @elem function fun
     *!       The function that was called at this level.
     *!     @elem mixed|void ... args
     *!       The arguments that the function was called with.
     *!   @endarray
     *!
     *!   The current call frame will be last in the array.
     *!
     *! @note
     *!   Please note that the frame order may be reversed in a later version
     *!   (than 7.1) of Pike to accomodate for deferred backtraces.
     *!
     *!   Note that the arguments reported in the backtrace are the current
     *!   values of the variables, and not the ones that were at call-time.
     *!   This can be used to hide sensitive information from backtraces
     *!   (eg passwords).
     *!
     *! @seealso
     *!   @[catch()], @[throw()]
     */
    PMOD_EXPORT
    PIKEFUN array(mixed) backtrace()
         efun;
         optflags OPT_EXTERNAL_DEPEND;
    {
      struct pike_frame *f, *of = 0;
      int size = 0;
      struct array *res = NULL;
    
      for (f = Pike_fp; f; f = f->next) {
        size++;
      }
    
      res = allocate_array_no_init(size, 0);
      push_array(res);
    
      for (f = Pike_fp; f && size; f = (of = f)->next) {
        struct object *o = low_clone(backtrace_frame_program);
        struct backtrace_frame_struct *bf;
    
        call_c_initializers(o);
    
        size--;
    
        res->item[size].u.object = o;
        res->item[size].type = PIKE_T_OBJECT;
        res->item[size].subtype = 0;
    
        bf = OBJ2_BACKTRACE_FRAME(o);
    
        if ((bf->prog = f->context.prog)) {
          add_ref(bf->prog);
          bf->pc = f->pc;
        }
    
        if ((bf->fun.u.object = f->current_object) &&
    	(bf->fun.u.object->prog)) {
          add_ref(bf->fun.u.object);
          bf->fun.subtype = f->fun;
          bf->fun.type = PIKE_T_FUNCTION;
        } else {
          bf->fun.u.integer = 0;
          bf->fun.subtype = NUMBER_DESTRUCTED;
          bf->fun.type = PIKE_T_INT;
        }
    
        if (f->locals) {
          INT32 numargs = DO_NOT_WARN((INT32) MINIMUM(f->num_args,
    						  Pike_sp - f->locals));
          if(of)
    	/* f->num_args can be too large, so this is necessary for some
    	 * reason. I don't know why. /mast */
    	numargs = DO_NOT_WARN((INT32)MINIMUM(f->num_args,of->locals - f->locals));
    
          numargs = MAXIMUM(numargs, 0);
    
          if (numargs) {
    	bf->args = allocate_array_no_init(numargs, 0);
    	assign_svalues_no_free(bf->args->item, f->locals, numargs, BIT_MIXED);
          }
        }
      }
      /* NOTE: res has already been pushed on the stack. */
    }
    
    #define INITIAL_BUF_LEN 4096
    
    /*! @module String
     */
    
    PIKECLASS Buffer
    /*! @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
     *!    @}
     */
    {
      CVAR struct string_builder str;
      CVAR int initial;
      
      void f_Buffer_get_copy( INT32 args );
      void f_Buffer_get( INT32 args );
      void f_Buffer_add( INT32 args );
    
    
      PIKEFUN void create( int|void size )
        /*! @decl void create()
        *!
        *!   Initializes a new buffer.
        *!   
        *!   If no @[initial_size] is specified, 4096 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.
        */
      {
        struct Buffer_struct *str = THIS;
        if( args )
          str->initial = MAXIMUM( size->u.integer, 512 );
        else
        {
          str->initial = 256;
          push_int(0);
        }
      }
    
      PIKEFUN string _sprintf( int flag, mapping flags )
      {
        switch( flag )
        {
          case 'O':
    	{
    	  struct pike_string *res;
    	  struct Buffer_struct *str = THIS;
    	  push_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 );
    	  res = Pike_sp[-1].u.string;
    	  Pike_sp--;
    	  RETURN res;
    	}
    
          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':
    	RETURN make_shared_binary_string("Buffer",6);
        }
        pop_n_elems( args );
        push_int( 0 );
        Pike_sp[-1].subtype = 1;
      }
    
      PIKEFUN mixed cast( string type )
      {
        struct pike_string *string_t;
        struct pike_string *int_t;
        MAKE_CONSTANT_SHARED_STRING( string_t, "string" ); 
        MAKE_CONSTANT_SHARED_STRING( int_t, "int" );
    
        if( type == string_t )
        {
          pop_n_elems( args );
          if( Pike_fp->current_object->refs != 1 )
    	f_Buffer_get_copy( 0 );
          else
    	f_Buffer_get( 0 );
          return;
        }
    
        if( type == int_t )
        {
          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;
        }
        Pike_error("Cannot cast to %s\n", type->str );
      }
      
      PIKEFUN object `+( string what )
      {
        struct Buffer_struct *str = THIS, *str2;
        struct object *res = clone_object( Buffer_program, 0 );
    
        if( str->str.s )
        {
          str2 = OBJ2_BUFFER( res );
    
          if( str2->str.s ) free_string_builder( &str2->str );
          *str2 = *str;
          init_string_builder_alloc( &str2->str,
    				 str->str.malloced,
    				 str->str.s->size_shift );
          MEMCPY( (void *)str2->str.s, (void *)str->str.s,
    	      str->str.malloced+sizeof(struct pike_string));
        }
        apply( res, "add", 1 );
        RETURN res;
      }
    
      PIKEFUN object `+=( string what )
      {
        f_Buffer_add( 1 );
        REF_RETURN fp->current_object;
      }
    
      PIKEFUN int add( string ... arg1 )
    /*! @decl void add(string ... data)
     *!
     *!   Adds @[data] to the buffer. Returns the size of the buffer.
     *!
     */
      {
        struct Buffer_struct *str = THIS;
        int j;
        struct pike_string *a;
    
        for( j = 0; j<args; j++ )
        {
          a = Pike_sp[-args+j].u.string;
          if( !str->str.s )
    	init_string_builder_alloc( &str->str, str->initial, a->size_shift );
          string_builder_shared_strcat( &str->str, a );
        }
        RETURN  str->str.s->len;
      }
    
      PIKEFUN string get_copy()
        /*! @decl string get_copy()
        *!
        *!   Get the data from the buffer. Significantly slower than @[get],
        *!   but does not clear the buffer.
        */
      {
        struct pike_string *str = THIS->str.s;
        if( str )
        {
          ptrdiff_t len = str->len;
          if( len > 0 )
          {
    	char *d = (char *)str->str;
    	switch( str->size_shift )
    	{
    	  case 0:
    	    RETURN make_shared_binary_string(d,len);
    	    break;
    	  case 1:
    	    RETURN make_shared_binary_string1((short*)d,len>>1);
    	    break;
    	  case 2:
    	    RETURN make_shared_binary_string2((int*)d,len>>2);
    	    break;
    	}
          }
        }
        push_text("");
        return;
      }
    
      PIKEFUN string get( )
        /*! @decl string get()
        *!
        *!   Get the data from the buffer.
        *!
        *! @note
        *!   This will clear the data in the buffer
        */
      {
        struct Buffer_struct *str = THIS;
        if( str->str.s )
        {
          struct pike_string *s = finish_string_builder( &str->str );
          str->str.malloced = 0;
          str->str.s = 0;
          RETURN s;
        }
        pop_n_elems(args);
        push_text("");
        return;
      }
    
      PIKEFUN int _sizeof()
      /*! @decl int _sizeof()
       *!
       *!   Returns the size of the buffer.
       */
      {
        struct Buffer_struct *str = THIS;
        RETURN str->str.s ? str->str.s->len : 0;
      }
    
      INIT
        {
          struct Buffer_struct *str = THIS;
          MEMSET( str, 0, sizeof( *str ) );
        }
    
      EXIT
        {
          struct Buffer_struct *str = THIS;
          if( str->str.s )
    	free_string_builder( &str->str );
        }
    }
    
    /*! @endmodule
     */
    
    /*! @module String
     */
    
    /*! @class Replace
     */
    PIKECLASS multi_string_replace
    {
      CVAR struct tupel
      {
        int prefix;
        struct pike_string *ind;
        struct pike_string *val;
      } *v;
      CVAR size_t v_sz;
      CVAR size_t sz;
      CVAR INT32 set_start[256];
      CVAR INT32 set_end[256];
    
      static int replace_sortfun(struct tupel *a,struct tupel *b)
      {
        return DO_NOT_WARN((int)my_quick_strcmp(a->ind, b->ind));
      }
    
      PIKEFUN void create(array(string)|void from_, array(string)|void to_)
      {
        int i;
        struct array *from;
        struct array *to;
        if (!args) {
          push_int(0);
          return;
        }
        if (!from_ || !to_) {
          Pike_error("Bad number of arguments to create().\n");
        }
        from = from_->u.array;
        to = to_->u.array;
        if (from->size != to->size) {
          Pike_error("Replace must have equal-sized from and to arrays.\n");
        }
        for (i = 0; i < (int)from->size; i++) {
          if (from->item[i].type != PIKE_T_STRING) {
    	Pike_error("Replace: from array is not an array(string).\n");
          }
          if (to->item[i].type != PIKE_T_STRING) {
    	Pike_error("Replace: to array is not an array(string).\n");
          }
        }
        if (THIS->v) {
          for (i = 0; i < (int)THIS->v_sz; i++) {
    	if (!THIS->v[i].ind) break;
    	free_string(THIS->v[i].ind);
    	THIS->v[i].ind = NULL;
    	free_string(THIS->v[i].val);
    	THIS->v[i].val = NULL;	
          }
        }
        if (THIS->v && (THIS->v_sz < (size_t)from->size)) {      
          free(THIS->v);
          THIS->v = NULL;
          THIS->v_sz = 0;
        }
        if (!THIS->v) {
          THIS->v = (struct tupel *)xalloc(sizeof(struct tupel) * from->size);
          THIS->v_sz = from->size;
        }
        for (i = 0; i < (int)from->size; i++) {
          copy_shared_string(THIS->v[i].ind, from->item[i].u.string);
          copy_shared_string(THIS->v[i].val, to->item[i].u.string);
          THIS->v[i].prefix = -2;	/* Uninitialized */
        }
        THIS->sz = from->size;
        fsort((char *)THIS->v, from->size, sizeof(struct tupel),
    	  (fsortfun)replace_sortfun);
    
        MEMSET(THIS->set_start, 0, sizeof(INT32)*256);
        MEMSET(THIS->set_end, 0, sizeof(INT32)*256);
    
        for (i = 0; i < (int)from->size; i++) {
          INT32 x = index_shared_string(THIS->v[from->size-1-i].ind, 0);
          if ((x >= 0) && (x < 256))
    	THIS->set_start[x] = from->size-1-i;
          x = index_shared_string(THIS->v[i].ind, 0);
          if ((x >= 0) && (x < 256))
    	THIS->set_end[x] = i+1;
        }
        pop_n_elems(args);
        push_int(0);
      }
    
      static int find_longest_prefix(char *str,
    				 ptrdiff_t len,
    				 int size_shift,
    				 struct tupel *v,
    				 INT32 a,
    				 INT32 b)
      {
        INT32 c,match=-1;
        ptrdiff_t tmp;
    
        while(a<b)
        {
          c=(a+b)/2;
          
          tmp=generic_quick_binary_strcmp(v[c].ind->str,
    				      v[c].ind->len,
    				      v[c].ind->size_shift,
    				      str,
    				      MINIMUM(len,v[c].ind->len),
    				      size_shift);
          if(tmp<0)
          {
    	INT32 match2=find_longest_prefix(str,
    					 len,
    					 size_shift,
    					 v,
    					 c+1,
    					 b);
    	if(match2!=-1) return match2;
    
    	while(1)
    	{
    	  if(v[c].prefix==-2)
    	  {
    	    v[c].prefix=find_longest_prefix(v[c].ind->str,
    					    v[c].ind->len,
    					    v[c].ind->size_shift,
    					    v,
    					    0 /* can this be optimized? */,
    					    c);
    	  }
    	  c=v[c].prefix;
    	  if(c<a || c<match) return match;
    
    	  if(!generic_quick_binary_strcmp(v[c].ind->str,
    					  v[c].ind->len,
    					  v[c].ind->size_shift,
    					  str,
    					  MINIMUM(len,v[c].ind->len),
    					  size_shift))
    	    return c;
    	}
          }
          else if(tmp>0)
          {
    	b=c;
          }
          else
          {
    	a=c+1; /* There might still be a better match... */
    	match=c;
          }
        }
        return match;
      }
    
      PIKEFUN string `()(string str)
      {
        struct string_builder ret;
        ptrdiff_t length = str->len;
        ptrdiff_t s;
        int *set_start = THIS->set_start;
        int *set_end = THIS->set_end;
        struct tupel *v = THIS->v;
        int num = THIS->sz;
    
        if (!num) {
          add_ref(str);
          RETURN str;
        }
    
        init_string_builder(&ret,str->size_shift);
    
        for(s=0;length > 0;)
        {
          INT32 a,b;
          ptrdiff_t ch;
    
          ch = index_shared_string(str, s);
          if((ch >= 0) && (ch < 256))
    	b = set_end[ch];
          else
    	b = num;
    
          if(b)
          {
    	if((ch >= 0) && (ch < 256))
    	  a = set_start[ch];
    	else
    	  a = 0;
    
    	a = find_longest_prefix(str->str+(s << str->size_shift),
    				length,
    				str->size_shift,
    				v, a, b);
    
    	if(a!=-1)
    	{
    	  ch = v[a].ind->len;
    	  if(!ch) ch=1;
    	  s += ch;
    	  length -= ch;
    	  string_builder_shared_strcat(&ret, v[a].val);
    	  continue;
    	}
          }
          string_builder_putchar(&ret,
    			     DO_NOT_WARN((INT32)ch));
          s++;
          length--;
        }
    
        RETURN finish_string_builder(&ret);
      }
    
      PIKEFUN array(string) _encode()
      {
        size_t i;
        for (i=0; i < THIS->sz; i++) {
          ref_push_string(THIS->v[i].ind);
        }
        f_aggregate(DO_NOT_WARN((INT32)THIS->sz));
        for (i=0; i < THIS->sz; i++) {
          ref_push_string(THIS->v[i].val);
        }
        f_aggregate(DO_NOT_WARN((INT32)THIS->sz));
        f_aggregate(2);
      }
    
      PIKEFUN void _decode(array(array(string)) encoded)
      {
        INT32 i;
    
        for (i=0; i < encoded->size; i++) {
          push_svalue(encoded->item + i);
          stack_swap();
        }
        pop_stack();
    
        f_multi_string_replace_create(i);
      }
    
      INIT
      {
        THIS->v = NULL;
        THIS->v_sz = 0;
        THIS->sz = 0;
      }
    
      EXIT
      {
        if (THIS->v) {
          int i;
          for (i = 0; i < (int)THIS->v_sz; i++) {
    	if (!THIS->v[i].ind) break;
    	free_string(THIS->v[i].ind);
    	THIS->v[i].ind = NULL;
    	free_string(THIS->v[i].val);
    	THIS->v[i].val = NULL;	
          }
          free(THIS->v);
        }
        THIS->v = NULL;
        THIS->v_sz = 0;
        THIS->sz = 0;
      }
    }
    
    /*! @endclass
     */
    
    /*! @class SingleReplace
     */
    PIKECLASS single_string_replace
    {
      CVAR SearchMojt mojt;
      CVAR struct pike_string *del;
      CVAR struct pike_string *to;
    
      INIT
      {
        THIS->mojt.vtab = NULL;
        THIS->mojt.data = NULL;
        THIS->del = NULL;
        THIS->to = NULL;
      }
    
      EXIT
      {
        if (THIS->mojt.vtab) {
          THIS->mojt.vtab->freeme(THIS->mojt.data);
          THIS->mojt.vtab = NULL;
          THIS->mojt.data = NULL;
        }
        if (THIS->del) {
          free_string(THIS->del);
          THIS->del = NULL;
        }
        if (THIS->to) {
          free_string(THIS->to);
          THIS->to = NULL;
        }
      }
    
      PIKEFUN void create(string|void del_, string|void to_)
      {
        struct pike_string *del;
        struct pike_string *to;
    
        /* Clean up... */
        exit_single_string_replace_struct();
        
        if (!del_) return;
    
        if (!to_) {
          SIMPLE_BAD_ARG_ERROR("String.SingleReplace->create", 2, "string");
        }
    
        if (del_->u.string == to_->u.string) {
          /* No-op... */
          return;
        }
    
        copy_shared_string(THIS->del, del = del_->u.string);
        copy_shared_string(THIS->to, to = to_->u.string);
    
        if (del->len) {
          THIS->mojt = simple_compile_memsearcher(del);
        }
      }
    
      /*** replace function ***/
      typedef char *(* replace_searchfunc)(void *,void *,size_t);
      PIKEFUN string `()(string str)
      {
        int shift;
        struct pike_string *del = THIS->del;
        struct pike_string *to = THIS->to;
        struct pike_string *ret = NULL;
    
        if (!str->len || !del || !to) {
          /* The result is already on the stack in the correct place... */
          return;
        }
    
        shift = MAXIMUM(str->size_shift, to->size_shift);
    
        if (!del->len) {
          int e, pos;
          ret = begin_wide_shared_string(str->len + to->len * (str->len-1),
    				     shift);
          low_set_index(ret, 0, index_shared_string(str, 0));
          for(pos=e=1;e<str->len;e++)
          {
    	pike_string_cpy(MKPCHARP_STR_OFF(ret,pos), to);
    	pos+=to->len;
    	low_set_index(ret,pos++,index_shared_string(str,e));
          }
        } else {
          char *s, *end, *tmp;
          replace_searchfunc f = (replace_searchfunc)0;
          void *mojt_data = THIS->mojt.data;
          PCHARP r;
    
          end = str->str+(str->len<<str->size_shift);
    
          switch(str->size_shift)
          {
          case 0: f = (replace_searchfunc)THIS->mojt.vtab->func0; break;
          case 1: f = (replace_searchfunc)THIS->mojt.vtab->func1; break;
          case 2: f = (replace_searchfunc)THIS->mojt.vtab->func2; break;
    #ifdef PIKE_DEBUG
          default: fatal("Illegal shift.\n");
    #endif
          }
    
          if(del->len == to->len)
          {
    	ret = begin_wide_shared_string(str->len, shift);
          } else {
    	INT32 delimiters = 0;
    
    	s = str->str;
    
    	while((s = f(mojt_data, s, (end-s)>>str->size_shift)))
    	{
    	  delimiters++;
    	  s += del->len << str->size_shift;
    	}
    
    	if (!delimiters) {
    	  /* The result is already on the stack in the correct place... */
    	  return;
    	}
    
    	ret = begin_wide_shared_string(str->len +
    				       (to->len-del->len)*delimiters, shift);
          }
    
          s = str->str;
          r = MKPCHARP_STR(ret);
    
          while((tmp = f(mojt_data, s, (end-s)>>str->size_shift)))
          {
    #ifdef PIKE_DEBUG
    	if(tmp + (del->len << str->size_shift) > end)
    	  fatal("generic_memory_search found a match beyond end of string!\n");
    #endif
    	generic_memcpy(r,MKPCHARP(s,str->size_shift),(tmp-s)>>str->size_shift);
    	INC_PCHARP(r,(tmp-s)>>str->size_shift);
    	pike_string_cpy(r,to);
    	INC_PCHARP(r,to->len);
    	s=tmp+(del->len << str->size_shift);
          }
          generic_memcpy(r,MKPCHARP(s,str->size_shift),(end-s)>>str->size_shift);
        }
        RETURN end_shared_string(ret);
      }
    
      PIKEFUN array(string) _encode()
      {
        if (THIS->del) {
          ref_push_string(THIS->del);
          ref_push_string(THIS->to);
          f_aggregate(2);
        } else {
          push_int(0);
        }
      }
    
      PIKEFUN void _decode(array(string)|int(0..0) encoded_)
      {    
        INT32 i = 0;
        if (encoded_->type == PIKE_T_ARRAY) {
          struct array *encoded = encoded_->u.array;
    
          for (i=0; i < encoded->size; i++) {
    	push_svalue(encoded->item + i);
    	stack_swap();
          }
        }
        pop_stack();
          
        f_single_string_replace_create(i);
      }
    }
    
    /*! @endclass
     */
    
    /*! @endmodule
     */
    
    void init_builtin(void)
    {
    INIT
    }
    
    void exit_builtin(void)
    {
    EXIT
    }