diff --git a/src/Makefile.in b/src/Makefile.in
index 5d82be553116e8107ab807a07ab0f991b5f64884..0fb51a2db2437bb67f1136ccd383022b04622417 100644
--- a/src/Makefile.in
+++ b/src/Makefile.in
@@ -1,5 +1,5 @@
 #
-# $Id: Makefile.in,v 1.132 1999/03/19 11:37:58 hubbe Exp $
+# $Id: Makefile.in,v 1.133 1999/03/20 02:31:54 grubba Exp $
 #
 
 # This line is needed on some machines.
@@ -499,6 +499,10 @@ $(SRCDIR)/peep_engine.c: $(SRCDIR)/peep.in $(TMP_BINDIR)/mkpeep.pike
 
 peep.o: $(SRCDIR)/peep_engine.c
 
+$(SRCDIR)/case_info.h: $(SRCDIR)/UnicodeData.txt $(TMP_BINDIR)/make_ci.pike
+	@if $(RUNPIKE) $(TMP_BINDIR)/make_ci.pike <$(SRCDIR)/UnicodeData.txt; \
+	then :; else echo '#include "dummy_ci.h"'; fi >$(SRCDIR)/case_info.h
+
 # make dependencies
 depend: $(SRCDIR)/language.c
 	gcc -MM $(PREFLAGS) $(SRCDIR)/*.c | $(TMP_BINDIR)/fixdepends.sh $(SRCDIR)
diff --git a/src/builtin_functions.c b/src/builtin_functions.c
index a5fa6df9a3cef404be9bcc3ca0aae491f9fc0c26..0c1035f11f72864ad3369f37ea136161754f5ead 100644
--- a/src/builtin_functions.c
+++ b/src/builtin_functions.c
@@ -5,7 +5,7 @@
 \*/
 /**/
 #include "global.h"
-RCSID("$Id: builtin_functions.c,v 1.158 1999/03/19 17:48:21 grubba Exp $");
+RCSID("$Id: builtin_functions.c,v 1.159 1999/03/20 02:31:55 grubba Exp $");
 #include "interpret.h"
 #include "svalue.h"
 #include "pike_macros.h"
@@ -152,42 +152,174 @@ void f_ctime(INT32 args)
   push_string(make_shared_string(ctime(&i)));
 }
 
-/* FIXME: wide char support ! */
+struct case_info {
+  int low;	/* low end of range. */
+  int mode;
+  int data;
+};
+
+#define CIM_NONE	0	/* Case-less */
+#define CIM_UPPER	1	/* Upper-case, lower-case in data */
+#define CIM_LOWER	2	/* Lower-case, upper-case in data */
+#define CIM_CASEBIT	3	/* Some case, case mask in data */
+#define CIM_CASEBITOFF	4	/* Same as above, but also offset by data */
+
+static struct case_info case_info[] = {
+#include "case_info.h"
+  { 0x10000, CIM_NONE, 0x0000, },	/* End sentinel. */
+};
+
+static struct case_info *find_ci(int c)
+{
+  static struct case_info *cache = NULL;
+  struct case_info ci = cache;
+  int lo = 0;
+  int hi = NELEM(case_info);
+
+  if ((c < 0) || (c > 0xffff))
+    return NULL;
+
+  if ((ci) && (ci[0].low <= c) && (ci[1].low > c)) {
+    return ci;
+  }
+
+  while (lo != hi-1) {
+    int mid = (lo + hi)/2;
+    if (case_info[mid].low < c) {
+      lo = mid;
+    } else if (case_info[mid].low == c) {
+      lo = mid;
+      break;
+    } else {
+      hi = mid;
+    }
+  }
+
+  return(cache = case_info + lo);
+}
+
+#define DO_LOWER_CASE(C) do {\
+    int c = C; \
+    struct case_info *ci = find_ci(c); \
+    if (ci) { \
+      switch(ci->mode) { \
+      case CIM_NONE: case CIM_LOWER: break; \
+      case CIM_UPPER: C = ci->data; break; \
+      case CIM_CASEBIT: C |= ci->data; break; \
+      case CIM_CASEBITOFF: C = ((c - ci->data) | ci->data) + ci->data; break; \
+      default: fatal("lower_case(): Unknown case_info mode: %d\n", ci->mode); \
+    } \
+  } while(0)
+
+#define DO_UPPER_CASE(C) do {\
+    int c = C; \
+    struct case_info *ci = find_ci(c); \
+    if (ci) { \
+      switch(ci->mode) { \
+      case CIM_NONE: case CIM_UPPER: break; \
+      case CIM_LOWER: C = ci->data; break; \
+      case CIM_CASEBIT: C &= ~ci->data; break; \
+      case CIM_CASEBITOFF: C = ((c - ci->data)& ~ci->data) + ci->data; break; \
+      default: fatal("lower_case(): Unknown case_info mode: %d\n", ci->mode); \
+    } \
+  } while(0)
+
 void f_lower_case(INT32 args)
 {
   INT32 i;
   struct pike_string *orig;
   struct pike_string *ret;
-  get_all_args("lower_case", args, "%S", &orig);
+  get_all_args("lower_case", args, "%W", &orig);
 
-  ret = begin_shared_string(orig->len);
-  MEMCPY(ret->str, orig->str, orig->len);
+  ret = begin_wide_shared_string(orig->len, orig->size_shift);
+
+  MEMCPY(ret->str, orig->str, orig->len << orig->size_shift);
+
+  i = orig->len;
+
+  if (!orig->size_shift) {
+    p_wchar0 *str = STR0(ret);
+
+    while(i--) {
+      DO_LOWER_CASE(str[i]);
+    }
+  } else if (orig->size_shift == 1) {
+    p_wchar1 *str = STR1(ret);
+
+    while(i--) {
+      DO_LOWER_CASE(str[i]);
+    }
+  } else if (orig->size_shift == 2) {
+    p_wchar2 *str = STR2(ret);
 
-  for (i = orig->len-1; i>=0; i--)
-    if (isupper(EXTRACT_UCHAR( ret->str + i)))
-      ret->str[i] = tolower(EXTRACT_UCHAR(ret->str+i));
+    while(i--) {
+      DO_LOWER_CASE(str[i]);
+    }
+  } else {
+    fatal("lower_case(): Bad string shift:%d\n", orig->size_shift);
+  }
 
   pop_n_elems(args);
   push_string(end_shared_string(ret));
 }
 
-/* FIXME: wide char support ! */
 void f_upper_case(INT32 args)
 {
   INT32 i;
   struct pike_string *orig;
   struct pike_string *ret;
-  get_all_args("upper_case",args,"%S",&orig);
+  int widen = 0;
+  get_all_args("upper_case",args,"%W",&orig);
 
-  ret=begin_shared_string(orig->len);
+  ret=begin_wide_shared_string(orig->len);
   MEMCPY(ret->str, orig->str, orig->len);
 
-  for (i = orig->len-1; i>=0; i--)
-    if (islower(EXTRACT_UCHAR(ret->str+i)))
-      ret->str[i] = toupper(EXTRACT_UCHAR(ret->str+i));
+  i = orig->len;
+
+  if (!orig->size_shift) {
+    p_wchar0 *str = STR0(ret);
+
+    while(i--) {
+      if (str[i] != 0xff) {
+	DO_UPPER_CASE(str[i]);
+      } else {
+	widen = 1;
+      }
+    }
+  } else if (orig->size_shift == 1) {
+    p_wchar1 *str = STR1(ret);
+
+    while(i--) {
+      DO_UPPER_CASE(str[i]);
+    }
+  } else if (orig->size_shift == 2) {
+    p_wchar2 *str = STR2(ret);
+
+    while(i--) {
+      DO_UPPER_CASE(str[i]);
+    }
+  } else {
+    fatal("lower_case(): Bad string shift:%d\n", orig->size_shift);
+  }
 
   pop_n_elems(args);
   push_string(end_shared_string(ret));
+
+  if (widen) {
+    /* Widen the string, and replace any 0xff's with 0x178's. */
+    orig = sp[-1].u.string;
+    ret = begin_wide_shared_string(orig->len, 1);
+
+    i = orig->len;
+
+    while(i--) {
+      if ((ret[i] = orig[i]) == 0xff) {
+	ret[i] = 0x178;
+      }
+    }
+    free_string(sp[-1].u.string);
+    sp[-1].u.string = end_shared_string(ret);
+  }
 }
 
 void f_random(INT32 args)