diff --git a/src/builtin.cmod b/src/builtin.cmod
index 1a2dbb5684c989b43dbd71ce05bf3c8571aefd93..491a8863de2a964a0bb2ed03a4cb74ab33bdab4c 100644
--- a/src/builtin.cmod
+++ b/src/builtin.cmod
@@ -2,7 +2,7 @@
 || 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.
-|| $Id: builtin.cmod,v 1.179 2005/11/14 19:57:54 nilsson Exp $
+|| $Id: builtin.cmod,v 1.180 2006/03/11 17:20:47 grubba Exp $
 */
 
 #include "global.h"
@@ -1938,246 +1938,111 @@ PIKECLASS Buffer
  */
 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];
+  CVAR struct replace_many_context ctx;
+  CVAR struct array *from;
+  CVAR struct array *to;
 
-  static int replace_sortfun(struct tupel *a,struct tupel *b)
-  {
-    return DO_NOT_WARN((int)my_quick_strcmp(a->ind, b->ind));
-  }
-
-  /*! @decl void create(array(string)|void from, array(string)|void to)
+  /*! @decl void create(array(string)|mapping(string:string)|void from, @
+   *!                   array(string)|string|void to)
    */
-  PIKEFUN void create(array(string)|void from, array(string)|void to)
+  PIKEFUN void create(array(string)|mapping(string:string)|void from_arg,
+		      array(string)|string|void to_arg)
   {
     int i;
+
+    if (THIS->from) free_array(THIS->from);
+    if (THIS->to) free_array(THIS->to);
+    if (THIS->ctx.v) free_replace_many_context(&THIS->ctx);
+
     if (!args) {
       push_int(0);
       return;
     }
-    /* FIXME: Why are from and to declared |void, when they aren't allowed
-     *        to be void?
-     *        /grubba 2004-09-02
-     */
-    if (!from || !to) {
-      Pike_error("Bad number of arguments to create().\n");
-    }
-    if (from->size != to->size) {
-      Pike_error("Replace must have equal-sized from and to arrays.\n");
+    if (from_arg && from_arg->type == T_MAPPING) {
+      if (to_arg) {
+	Pike_error("Bad number of arguments to create().\n");
+      }
+      THIS->from = mapping_indices(from_arg->u.mapping);
+      THIS->to = mapping_values(from_arg->u.mapping);
+      pop_n_elems(args);
+      args = 0;
+    } else {
+      /* FIXME: Why is from declared |void, when it isn't allowed
+       *        to be void?
+       *        /grubba 2004-09-02
+       */
+      if (!from_arg || !to_arg) {
+	Pike_error("Bad number of arguments to create().\n");
+      }
+      pop_n_elems(args-2);
+      args = 2;
+      if (from_arg->type != T_ARRAY) {
+	SIMPLE_BAD_ARG_ERROR("Replace", 1,
+			     "array(string)|mapping(string:string)");
+      }
+      if (to_arg->type == T_STRING) {
+	push_int(from_arg->u.array->size);
+	stack_swap();
+	f_allocate(2);
+      }
+      if (to_arg->type != T_ARRAY) {
+	SIMPLE_BAD_ARG_ERROR("Replace", 2, "array(string)|string");
+      }
+      if (from_arg->u.array->size != to_arg->u.array->size) {
+	Pike_error("Replace must have equal-sized from and to arrays.\n");
+      }
+      add_ref(THIS->from = from_arg->u.array);
+      add_ref(THIS->to = to_arg->u.array);
     }
 
-    if (!from->size) {
+    if (!THIS->from->size) {
       /* Enter no-op mode. */
-      THIS->sz = 0;
       pop_n_elems(args);
       push_int(0);
       return;
     }
 
-    if( (from->type_field & ~BIT_STRING) &&
-	(array_fix_type_field(from) & ~BIT_STRING) )
-      SIMPLE_BAD_ARG_ERROR("Replace", 1, "array(string)");
+    if( (THIS->from->type_field & ~BIT_STRING) &&
+	(array_fix_type_field(THIS->from) & ~BIT_STRING) )
+      SIMPLE_BAD_ARG_ERROR("Replace", 1,
+			   "array(string)|mapping(string:string)");
 
-    if( (to->type_field & ~BIT_STRING) &&
-	(array_fix_type_field(to) & ~BIT_STRING) )
-      SIMPLE_BAD_ARG_ERROR("Replace", 2, "array(string)");
+    if( (THIS->to->type_field & ~BIT_STRING) &&
+	(array_fix_type_field(THIS->to) & ~BIT_STRING) )
+      SIMPLE_BAD_ARG_ERROR("Replace", 2, "array(string)|string");
 
-    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);
+    compile_replace_many(&THIS->ctx, THIS->from, THIS->to, 1);
 
-    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;
-
-    check_c_stack(2048);
-
-    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;
-  }
-
   /*! @decl string `()(string str)
    */
   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;
-    ONERROR uwp;
-
-    if (!num) {
+    if (!THIS->ctx.v) {
       add_ref(str);
       RETURN str;
     }
 
-    init_string_builder(&ret,str->size_shift);
-    SET_ONERROR(uwp, free_string_builder, &ret);
-
-    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--;
-    }
-
-    UNSET_ONERROR(uwp);
-
-    RETURN finish_string_builder(&ret);
+    RETURN execute_replace_many(&THIS->ctx, str);
   }
 
-  /*! @decl array(string) _encode()
+  /*! @decl array(array(string)) _encode()
    */
-  PIKEFUN array(string) _encode()
+  PIKEFUN array(array(string)) _encode()
   {
-    size_t i;
-    for (i=0; i < THIS->sz; i++) {
-      ref_push_string(THIS->v[i].ind);
+    if (THIS->from) {
+      ref_push_array(THIS->from);
+    } else {
+      push_undefined();
     }
-    f_aggregate(DO_NOT_WARN((INT32)THIS->sz));
-    for (i=0; i < THIS->sz; i++) {
-      ref_push_string(THIS->v[i].val);
+    if (THIS->to) {
+      ref_push_array(THIS->to);
+    } else {
+      push_undefined();
     }
-    f_aggregate(DO_NOT_WARN((INT32)THIS->sz));
     f_aggregate(2);
   }
 
@@ -2186,7 +2051,6 @@ PIKECLASS multi_string_replace
   PIKEFUN void _decode(array(array(string)) encoded)
   {
     INT32 i;
-
     for (i=0; i < encoded->size; i++) {
       push_svalue(encoded->item + i);
       stack_swap();
@@ -2198,27 +2062,16 @@ PIKECLASS multi_string_replace
 
   INIT
   {
-    THIS->v = NULL;
-    THIS->v_sz = 0;
-    THIS->sz = 0;
+    MEMSET(&THIS->ctx, 0, sizeof(struct replace_many_context));
+    THIS->from = NULL;
+    THIS->to = NULL;
   }
 
   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;
+    if (THIS->from) free_array(THIS->from);
+    if (THIS->to) free_array(THIS->to);
+    free_replace_many_context(&THIS->ctx);
   }
 }