diff --git a/.gitattributes b/.gitattributes
index 995051f52b37a5d2052d7586602d7a4ae9522c0a..f05075de30ba258e3ce33aedf4766967f74cbd45 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -398,6 +398,7 @@ testfont binary
 /src/pike_types.h foreign_ident
 /src/port.c foreign_ident
 /src/port.h foreign_ident
+/src/preprocessor.h foreign_ident
 /src/program.c foreign_ident
 /src/program.h foreign_ident
 /src/program_areas.h foreign_ident
diff --git a/src/cpp.c b/src/cpp.c
index 64005b4c8076fd2063e427e9cc4ffe345fcba427..5aca51294f3028efbaad55b0a980ca5b4f1193a5 100644
--- a/src/cpp.c
+++ b/src/cpp.c
@@ -5,7 +5,7 @@
 \*/
 
 /*
- * $Id: cpp.c,v 1.45 1999/02/26 23:29:46 grubba Exp $
+ * $Id: cpp.c,v 1.46 1999/02/27 21:52:37 grubba Exp $
  */
 #include "global.h"
 #include "language.h"
@@ -43,23 +43,6 @@
 #define PUTC(C) do { \
  int c_=(C); if(OUTP() || c_=='\n') string_builder_putchar(&this->buf, c_); }while(0)
 
-#define STRCAT(STR,LEN) do {				\
-  INT32 x_,len_=(LEN);					\
-  char *str_=(STR);					\
-  if(OUTP())						\
-    string_builder_binary_strcat(&this->buf, str_,len_);	\
-  else							\
-    for(x_=0;x_<len_;x_++)				\
-      if(str_[x_]=='\n')				\
-        string_builder_putchar(&this->buf, '\n');		\
-}while(0)
-
-#define CHECKWORD(X) \
- (!strncmp(X,data+pos,strlen(X)) && !isidchar(data[pos+strlen(X)]))
-#define WGOBBLE(X) (CHECKWORD(X) ? (pos+=strlen(X)),1 : 0)
-#define GOBBLEOP(X) \
- ((!strncmp(X,data+pos,strlen(X))) ? (pos+=strlen(X)),1 : 0)
-
 #define MAX_ARGS            255
 #define DEF_ARG_STRINGIFY   0x100000
 #define DEF_ARG_NOPRESPACE  0x200000
@@ -82,7 +65,7 @@ struct define_part
 };
 
 struct define_argument {
-  char *arg;
+  PCHARP arg;
   INT32 len;
 };
 
@@ -121,9 +104,6 @@ struct cpp
 struct define *defined_macro =0;
 struct define *constant_macro =0;
 
-static INT32 calc(struct cpp *,char*,INT32,INT32);
-static INT32 calc1(struct cpp *,char*,INT32,INT32);
-
 void cpp_error(struct cpp *this,char *err)
 {
   this->compile_errors++;
@@ -263,13 +243,13 @@ static void simple_add_define(struct cpp *this,
   } while(0)
 
 #define SKIPWHITE() do {					\
-    if(!isspace(((unsigned char *)data)[pos])) break;		\
+    if(!WC_ISSPACE(data[pos])) break;				\
     if(data[pos]=='\n') { PUTNL(); this->current_line++; }	\
     pos++;							\
   } while(1)
 
 #define SKIPSPACE() \
-  do { while(isspace(((unsigned char *)data)[pos]) && data[pos]!='\n') pos++; \
+  do { while(WC_ISSPACE(data[pos]) && data[pos]!='\n') pos++; \
   } while (0)
 
 #define SKIPCOMMENT()	do{				\
@@ -323,7 +303,7 @@ static void simple_add_define(struct cpp *this,
     break;					\
     						\
   default:					\
-    C=((unsigned char *)data)[pos];		\
+    C = data[pos];				\
   }						\
 }while (0)
 
@@ -361,7 +341,7 @@ while(1)					\
   }						\
 						\
   default:					\
-    string_builder_putchar(&nf, ((unsigned char *)data)[pos]);	\
+    string_builder_putchar(&nf, data[pos]);	\
     continue;					\
   }						\
   pos++;					\
@@ -401,7 +381,7 @@ while(1)						\
     pos++;                                              \
 							\
   default:						\
-    if(outp) string_builder_putchar(&nf, ((unsigned char *)data)[pos-1]);	\
+    if(outp) string_builder_putchar(&nf, data[pos-1]);	\
     continue;						\
   }							\
   break;						\
@@ -440,73 +420,13 @@ while(1)					\
     PUTNL();					\
     this->current_line++;			\
   default:					\
-    string_builder_putchar(&nf, ((unsigned char *)data)[pos]);	\
+    string_builder_putchar(&nf, data[pos]);	\
     continue;					\
   }						\
   pos++;					\
   break;					\
 }
 
-void PUSH_STRING(char *str,
-		 INT32 len,
-		 struct string_builder *buf)
-{
-  INT32 p2;
-  string_builder_putchar(buf, '"');
-  for(p2=0;p2<len;p2++)
-  {
-    switch(str[p2])
-    {
-    case '\n':
-      string_builder_putchar(buf, '\\');
-      string_builder_putchar(buf, 'n');
-      break;
-      
-    case '\t':
-      string_builder_putchar(buf, '\\');
-      string_builder_putchar(buf, 't');
-      break;
-      
-    case '\r':
-      string_builder_putchar(buf, '\\');
-      string_builder_putchar(buf, 'r');
-      break;
-      
-    case '\b':
-      string_builder_putchar(buf, '\\');
-      string_builder_putchar(buf, 'b');
-      break;
-      
-    case '\\':
-    case '"':
-      string_builder_putchar(buf, '\\');
-      string_builder_putchar(buf, ((unsigned char *)str)[p2]);
-      break;
-      
-    default:
-      if(isprint(EXTRACT_UCHAR(str+p2)))
-      {
-	string_builder_putchar(buf, ((unsigned char *)str)[p2]);
-      }
-      else
-      {
-	int c=EXTRACT_UCHAR(str+p2);
-	string_builder_putchar(buf, '\\');
-	string_builder_putchar(buf, ((c>>6)&7)+'0');
-	string_builder_putchar(buf, ((c>>3)&7)+'0');
-	string_builder_putchar(buf, (c&7)+'0');
-	if(EXTRACT_UCHAR(str+p2+1)>='0' && EXTRACT_UCHAR(str+p2+1)<='7')
-	{
-	  string_builder_putchar(buf, '"');
-	  string_builder_putchar(buf, '"');
-	}
-      }
-      break;
-    }
-  }
-  string_builder_putchar(buf, '"');
-}
-
 #define FINDTOK() 				\
   do {						\
   SKIPSPACE();					\
@@ -542,1381 +462,34 @@ void PUSH_STRING(char *str,
   break;					\
   }while(1)
 
-static INLINE int find_end_parenthesis(struct cpp *this,
-				       char *data,
-				       INT32 len,
-				       INT32 pos) /* position of first " */
-{
-  while(1)
-  {
-    if(pos+1>=len)
-    {
-      cpp_error(this,"End of file while looking for end parenthesis.");
-      return pos;
-    }
-
-    switch(data[pos++])
-    {
-    case '\n': PUTNL(); this->current_line++; break;
-    case '\'': FIND_END_OF_CHAR();  break;
-    case '"':  FIND_END_OF_STRING();  break;
-    case '(':  pos=find_end_parenthesis(this, data, len, pos); break;
-    case ')':  return pos;
-    }
-  }
-}
-
-static INT32 low_cpp(struct cpp *this,
-		    char *data,
-		    INT32 len,
-		    int flags)
-{
-  INT32 pos, tmp, e, tmp2;
-  
-  for(pos=0;pos<len;)
-  {
-/*    fprintf(stderr,"%c",data[pos]);
-    fflush(stderr); */
-
-    switch(data[pos++])
-    {
-    case '\n':
-      if(flags & CPP_END_AT_NEWLINE) return pos-1;
-
-/*      fprintf(stderr,"CURRENT LINE: %d\n",this->current_line); */
-      this->current_line++;
-      PUTNL();
-      goto do_skipwhite;
-
-    case '\t':
-    case ' ':
-    case '\r':
-      PUTC(' ');
-      
-    do_skipwhite:
-      while(data[pos]==' ' || data[pos]=='\r' || data[pos]=='\t')
-	pos++;
-      break;
-
-      /* Minor optimization */
-      case '<':
-	if(data[pos]=='<'  &&
-	   data[pos+1]=='<'  &&
-	   data[pos+2]=='<'  &&
-	   data[pos+3]=='<'  &&
-	   data[pos+4]=='<'  &&
-	   data[pos+5]=='<')
-	  cpp_error(this,"CVS conflict detected");
-	
-      case '!': case '@': case '$': case '%': case '^': case '&':
-      case '*': case '(': case ')': case '-': case '=': case '+':
-      case '{': case '}': case ':': case '?': case '`': case ';':
-      case '>': case ',': case '.': case '~': case '[':
-      case ']': case '|':
-	PUTC(((unsigned char *)data)[pos-1]);
-      break;
-      
-    default:
-      if(OUTP() && isidchar(data[pos-1]))
-      {
-	struct pike_string *s=0;
-	struct define *d=0;
-	tmp=pos-1;
-	while(isidchar(data[pos])) pos++;
-
-	if(flags & CPP_DO_IF)
-	{
-	  if(pos-tmp == 7 && !strncmp("defined",data+tmp, 7))
-	  {
-	    d=defined_macro;
-	  }
-	  else if((pos-tmp == 4 && !strncmp("efun",data+tmp, 4)) ||
-		  (pos-tmp == 8 && !strncmp("constant",data+tmp,8)))
-	  {
-	    d=constant_macro;
-	  }
-	  else
-	  {
-	    goto do_find_define;
-	  }
-	}else{
-	do_find_define:
-	  if((s=binary_findstring(data+tmp, pos-tmp)))
-	  {
-	    d=find_define(s);
-	  }
-	}
-	  
-	if(d && !d->inside)
-	{
-	  int arg=0;
-	  struct string_builder tmp;
-	  struct define_argument arguments [MAX_ARGS];
-	  
-	  if(s) add_ref(s);
-	  
-	  if(d->args>=0)
-	  {
-	    SKIPWHITE();
-	    
-	    if(!GOBBLE('('))
-	    {
-	      cpp_error(this,"Missing '(' in macro call.");
-	      break;
-	    }
-	    
-	    for(arg=0;arg<d->args;arg++)
-	    {
-	      if(arg && data[pos]==',')
-	      {
-		pos++;
-		SKIPWHITE();
-	      }else{
-		SKIPWHITE();
-		if(data[pos]==')')
-		{
-		  char buffer[1024];
-		  sprintf(buffer,"Too few arguments to macro %s, expected %d.",d->link.s->str,d->args);
-		  cpp_error(this,buffer);
-		  break;
-		}
-	      }
-	      arguments[arg].arg=data + pos;
-
-	      
-	      while(1)
-	      {
-		if(pos+1>len)
-		{
-		  cpp_error(this,"End of file in macro call.");
-		  break;
-		}
-		
-		switch(data[pos++])
-		{
-		case '\n':
-		  this->current_line++;
-		  PUTNL();
-		default: continue;
-		  
-		case '"':
-		  FIND_END_OF_STRING();
-		  continue;
-		  
-		case '\'':
-		  FIND_END_OF_CHAR();
-		  continue;
-		  
-		case '(':
-		  pos=find_end_parenthesis(this, data, len, pos);
-		  continue;
-		  
-		case ')': 
-		case ',': pos--;
-		  break;
-		}
-		break;
-	      }
-	      arguments[arg].len=data+pos-arguments[arg].arg;
-	    }
-	    SKIPWHITE();
-	    if(!GOBBLE(')'))
-	      cpp_error(this,"Missing ) in macro call.");
-	  }
-	  
-	  if(d->args >= 0 && arg != d->args)
-	    cpp_error(this,"Wrong number of arguments to macro.");
-	  
-	  init_string_builder(&tmp, 0);
-	  if(d->magic)
-	  {
-	    d->magic(this, d, arguments, &tmp);
-	  }else{
-	    string_builder_binary_strcat(&tmp, d->first->str, d->first->len);
-	    for(e=0;e<d->num_parts;e++)
-	    {
-	      char *a;
-	      INT32 l;
-	      
-	      if((d->parts[e].argument & DEF_ARG_MASK) < 0 || 
-		 (d->parts[e].argument & DEF_ARG_MASK) >= arg)
-	      {
-		cpp_error(this,"Macro not expanded correctly.");
-		continue;
-	      }
-	      
-	      a=arguments[d->parts[e].argument&DEF_ARG_MASK].arg;
-	      l=arguments[d->parts[e].argument&DEF_ARG_MASK].len;
-	      
-	      if(!(d->parts[e].argument & DEF_ARG_NOPRESPACE))
-		string_builder_putchar(&tmp, ' ');
-	      
-	      if(d->parts[e].argument & DEF_ARG_STRINGIFY)
-	      {
-		PUSH_STRING(a,l,&tmp);
-	      }else{
-		if(DEF_ARG_NOPRESPACE)
-		  while(l && isspace(*(unsigned char *)a))
-		    a++,l--;
-		
-		if(DEF_ARG_NOPOSTSPACE)
-		  while(l && isspace(*(unsigned char *)(a+l-1)))
-		    l--;
-
-		if(d->parts[e].argument & (DEF_ARG_NOPRESPACE | DEF_ARG_NOPOSTSPACE))
-		{
-		  
-		  string_builder_binary_strcat(&tmp, a, l);
-		}else{
-		  struct string_builder save;
-		  INT32 line=this->current_line;
-		  save=this->buf;
-		  this->buf=tmp;
-		  low_cpp(this, a, l,
-			  flags & ~(CPP_EXPECT_ENDIF | CPP_EXPECT_ELSE));
-		  tmp=this->buf;
-		  this->buf=save;
-		  this->current_line=line;
-		}
-	      }
-	      
-	      if(!(d->parts[e].argument & DEF_ARG_NOPOSTSPACE))
-		string_builder_putchar(&tmp, ' ');
-	      
-	      string_builder_binary_strcat(&tmp, d->parts[e].postfix->str,
-					   d->parts[e].postfix->len);
-	    }
-	  }
-	  
-	  /* FIXME */
-	  for(e=0;e<(long)tmp.s->len;e++)
-	    if(tmp.s->str[e]=='\n')
-	      tmp.s->str[e]=' ';
-
-	  if(s) d->inside=1;
-	  
-	  string_builder_putchar(&tmp, 0);
-	  tmp.s->len--;
-	  
-	  low_cpp(this, tmp.s->str, tmp.s->len, 
-		  flags & ~(CPP_EXPECT_ENDIF | CPP_EXPECT_ELSE));
-	  
-	  if(s)
-	  {
-	    if((d=find_define(s)))
-	      d->inside=0;
-	    
-	    free_string(s);
-	  }
-
-	  free_string_builder(&tmp);
-	  break;
-	}else{
-	  if(flags & CPP_DO_IF)
-	  {
-	    STRCAT(" 0 ", 3);
-	  }else{
-	    STRCAT(data+tmp, pos-tmp);
-	  }
-	}
-      }else{
-	PUTC(((unsigned char *)data)[pos-1]);
-      }
-      break;
-      
-    case '0': case '1': case '2': case '3': case '4':
-    case '5': case '6': case '7': case '8': case '9':
-      PUTC(((unsigned char *)data)[pos-1]);
-      while(data[pos]>='0' && data[pos]<='9') PUTC(data[pos++]);
-      break;
-
-    case '"':
-      FIXSTRING(this->buf,OUTP());
-      break;
-
-    case '\'':
-      tmp=pos-1;
-      FIND_END_OF_CHAR();
-      STRCAT(data+tmp, pos-tmp);
-      break;
-
-    case '/':
-      if(data[pos]=='/')
-      {
-	FIND_EOL();
-	break;
-      }
-
-      if(data[pos]=='*')
-      {
-	PUTC(' ');
-	SKIPCOMMENT();
-	break;
-      }
-
-      PUTC(((unsigned char *)data)[pos-1]);
-      break;
-
-  case '#':
-    if(GOBBLE('!'))
-    {
-      FIND_EOL();
-      break;
-    }
-    SKIPSPACE();
-
-    switch(data[pos])
-    {
-    case 'l':
-      if(WGOBBLE("line"))
-	{
-	  /* FIXME: Why not use SKIPSPACE()? */
-	  while(data[pos]==' ' || data[pos]=='\t') pos++;
-	}else{
-	  goto unknown_preprocessor_directive;
-	}
-      /* Fall through */
-      
-    case '0': case '1': case '2': case '3': case '4':
-    case '5': case '6': case '7': case '8': case '9':
-    {
-      char *foo=data+pos;
-      this->current_line=STRTOL(foo, &foo, 10)-1;
-      string_builder_putchar(&this->buf, '#');
-      string_builder_binary_strcat(&this->buf, data+pos, foo-(data+pos));
-      pos=foo-data;
-      SKIPSPACE();
-      
-      if(data[pos]=='"')
-      {
-	struct string_builder nf;
-	init_string_builder(&nf, 0);
-	
-	READSTRING(nf);
-
-	free_string(this->current_file);
-	this->current_file = finish_string_builder(&nf);
-
-	string_builder_putchar(&this->buf, ' ');
-	PUSH_STRING(this->current_file->str,this->current_file->len,&this->buf);
-      }
-      
-      FIND_EOL();
-      break;
-    }
-
-      case '"':
-      {
-	struct string_builder nf;
-	init_string_builder(&nf, 0);
-	
-	READSTRING2(nf);
-	if(OUTP())
-	{
-	  PUSH_STRING(nf.s->str,
-		      nf.s->len,
-		      &this->buf);
-	}
-	free_string_builder(&nf);
-	break;
-      }
-
-      case 's':
-	if(WGOBBLE("string"))
-	{
-	  tmp2=1;
-	  goto do_include;
-	}
-	
-      goto unknown_preprocessor_directive;
-
-    case 'i': /* include, if, ifdef */
-      if(WGOBBLE("include"))
-      {
-	tmp2=0;
-      do_include:
-	{
-	  struct svalue *save_sp=sp;
-	  SKIPSPACE();
-	  
-	  check_stack(3);
-	  
-	  switch(data[pos++])
-	  {
-	    case '"':
-	      {
-		struct string_builder nf;
-		init_string_builder(&nf, 0);
-		pos--;
-		READSTRING(nf);
-		push_string(finish_string_builder(&nf));
-		ref_push_string(this->current_file);
-		push_int(1);
-		break;
-	      }
-
-	    case '<':
-	      {
-		INT32 tmp=pos;
-		while(data[pos]!='>')
-		{
-		  if(data[pos]=='\n')
-		  {
-		    cpp_error(this,"Expecting '>' in include.");
-		    break;
-		  }
-		      
-		  pos++;
-		}
-		push_string(make_shared_binary_string(data+tmp, pos-tmp));
-		ref_push_string(this->current_file);
-		pos++;
-		push_int(0);
-		break;
-	      }
-
-	    default:
-	      cpp_error(this,"Expected file to include.");
-	      break;
-	    }
-
-	  if(sp==save_sp) break;
-
-	  if(OUTP())
-	  {
-	    struct pike_string *new_file;
-
-	    SAFE_APPLY_MASTER("handle_include",3);
-	  
-	    if(sp[-1].type != T_STRING)
-	    {
-	      cpp_error(this,"Couldn't include file.");
-	      pop_n_elems(sp-save_sp);
-	      break;
-	    }
-	    
-	    new_file=sp[-1].u.string;
-	    
-	    assign_svalue_no_free(sp,sp-1);
-	    sp++;
-	    
-	    SAFE_APPLY_MASTER("read_include",1);
-	    
-	    if(sp[-1].type != T_STRING)
-	    {
-	      cpp_error(this,"Couldn't read include file.");
-	      pop_n_elems(sp-save_sp);
-	      break;
-	    }
-	    
-	    {
-	      char buffer[47];
-	      struct pike_string *save_current_file;
-	      INT32 save_current_line;
-
-	      save_current_file=this->current_file;
-	      save_current_line=this->current_line;
-	      copy_shared_string(this->current_file,new_file);
-	      this->current_line=1;
-	      
-	      string_builder_binary_strcat(&this->buf, "# 1 ", 4);
-	      PUSH_STRING(new_file->str,new_file->len, & this->buf);
-	      string_builder_putchar(&this->buf, '\n');
-	      if(tmp2)
-	      {
-		PUSH_STRING(sp[-1].u.string->str,
-			    sp[-1].u.string->len,
-			    &this->buf);
-	      }else{
-		low_cpp(this,
-			sp[-1].u.string->str,
-			sp[-1].u.string->len,
-			flags&~(CPP_EXPECT_ENDIF | CPP_EXPECT_ELSE));
-	      }
-	      
-	      free_string(this->current_file);
-	      this->current_file=save_current_file;
-	      this->current_line=save_current_line;
-	      
-	      sprintf(buffer,"# %d ",this->current_line);
-	      string_builder_binary_strcat(&this->buf, buffer, strlen(buffer));
-	      PUSH_STRING(this->current_file->str,this->current_file->len,& this->buf);
-	      string_builder_putchar(&this->buf, '\n');
-	    }
-	  }
-
-	  pop_n_elems(sp-save_sp);
-	  
-	  break;
-	}
-      }
-
-      if(WGOBBLE("if"))
-      {
-	struct string_builder save,tmp;
-	INT32 nflags=CPP_EXPECT_ELSE | CPP_EXPECT_ENDIF;
-	
-	if(!OUTP())
-	  nflags|=CPP_REALLY_NO_OUTPUT;
-	
-	save=this->buf;
-	init_string_builder(&this->buf, 0);
-	pos+=low_cpp(this,data+pos,len-pos,CPP_END_AT_NEWLINE|CPP_DO_IF);
-	tmp=this->buf;
-	this->buf=save;
-	
-	string_builder_putchar(&tmp, 0);
-	tmp.s->len--;
-	
-	calc(this,tmp.s->str,tmp.s->len,0);
-	free_string_builder(&tmp);
-	if(IS_ZERO(sp-1)) nflags|=CPP_NO_OUTPUT;
-	pop_stack();
-	pos+=low_cpp(this,data+pos,len-pos,nflags);
-	break;
-      }
-
-      if(WGOBBLE("ifdef"))
-	{
-	  INT32 namestart,nflags;
-	  struct pike_string *s;
-	  SKIPSPACE();
-
-	  if(!isidchar(data[pos]))
-	    cpp_error(this,"#ifdef what?\n");
-
-	  namestart=pos;
-	  while(isidchar(data[pos])) pos++;
-	  nflags=CPP_EXPECT_ELSE | CPP_EXPECT_ENDIF | CPP_NO_OUTPUT;
-
-	  if(!OUTP())
-	    nflags|=CPP_REALLY_NO_OUTPUT;
-
-	  if((s=binary_findstring(data+namestart,pos-namestart)))
-	    if(find_define(s))
-	      nflags&=~CPP_NO_OUTPUT;
-
-	  pos+=low_cpp(this,data+pos,len-pos,nflags);
-	  break;
-	}
-
-      if(WGOBBLE("ifndef"))
-	{
-	  INT32 namestart,nflags;
-	  struct pike_string *s;
-	  SKIPSPACE();
-
-	  if(!isidchar(data[pos]))
-	    cpp_error(this,"#ifndef what?");
-
-	  namestart=pos;
-	  while(isidchar(data[pos])) pos++;
-	  nflags=CPP_EXPECT_ELSE | CPP_EXPECT_ENDIF;
-
-	  if(!OUTP())
-	    nflags|=CPP_REALLY_NO_OUTPUT;
-
-	  if((s=binary_findstring(data+namestart,pos-namestart)))
-	    if(find_define(s))
-	      nflags|=CPP_NO_OUTPUT;
-
-	  pos+=low_cpp(this,data+pos,len-pos,nflags);
-	  break;
-	}
-
-      goto unknown_preprocessor_directive;
-
-    case 'e': /* endif, else, elif, error */
-      if(WGOBBLE("endif"))
-      {
-	if(!(flags & CPP_EXPECT_ENDIF))
-	  cpp_error(this,"Unmatched #endif");
-
-	return pos;
-      }
-
-      if(WGOBBLE("else"))
-	{
-	  if(!(flags & CPP_EXPECT_ELSE))
-	    cpp_error(this,"Unmatched #else");
-
-	  flags&=~CPP_EXPECT_ELSE;
-	  flags|=CPP_EXPECT_ENDIF;
-
-	  if(flags & CPP_NO_OUTPUT)
-	    flags&=~CPP_NO_OUTPUT;
-	  else
-	    flags|=CPP_NO_OUTPUT;
-
-	  break;
-	}
-
-      if(WGOBBLE("elif") || WGOBBLE("elseif"))
-      {
-	if(!(flags & CPP_EXPECT_ELSE))
-	  cpp_error(this,"Unmatched #elif");
-	
-	flags|=CPP_EXPECT_ENDIF;
-	
-	if(flags & CPP_NO_OUTPUT)
-	{
-	  struct string_builder save,tmp;
-	  save=this->buf;
-	  init_string_builder(&this->buf, 0);
-	  pos+=low_cpp(this,data+pos,len-pos,CPP_END_AT_NEWLINE|CPP_DO_IF);
-	  tmp=this->buf;
-	  this->buf=save;
-	  
-	  string_builder_putchar(&tmp, 0);
-	  tmp.s->len--;
-	  
-	  calc(this,tmp.s->str,tmp.s->len,0);
-	  free_string_builder(&tmp);
-	  if(!IS_ZERO(sp-1)) flags&=~CPP_NO_OUTPUT;
-	  pop_stack();
-	}else{
-	  FIND_EOL();
-	  flags|= CPP_NO_OUTPUT | CPP_REALLY_NO_OUTPUT;
-	}
-	break;
-      }
-
-      if(WGOBBLE("error"))
-	{
-          INT32 foo;
-          SKIPSPACE();
-          foo=pos;
-          FIND_EOL();
-	  pos++;
-	  if(OUTP())
-	  {
-	    push_string(make_shared_binary_string(data+foo,pos-foo));
-	    cpp_error(this,sp[-1].u.string->str);
-	  }
-	  break;
-	}
-      
-      goto unknown_preprocessor_directive;
-
-    case 'd': /* define */
-      if(WGOBBLE("define"))
-	{
-	  struct string_builder str;
-	  INT32 namestart, tmp3, nameend, argno=-1;
-	  struct define *def;
-	  struct svalue *partbase,*argbase=sp;
-
-	  SKIPSPACE();
-
-	  namestart=pos;
-	  if(!isidchar(data[pos]))
-	    cpp_error(this,"Define what?");
-
-	  while(isidchar(data[pos])) pos++;
-	  nameend=pos;
-
-	  if(GOBBLE('('))
-	    {
-	      argno=0;
-	      SKIPWHITE();
-
-	      while(data[pos]!=')')
-		{
-		  INT32 tmp2;
-		  if(argno)
-		    {
-		      if(!GOBBLE(','))
-			cpp_error(this,"Expecting comma in macro definition.");
-		      SKIPWHITE();
-		    }
-		  tmp2=pos;
-
-		  if(!isidchar(data[pos]))
-		  {
-		    cpp_error(this,"Expected argument for macro.");
-		    break;
-		  }
-
-		  while(isidchar(data[pos])) pos++;
-		  check_stack(1);
-		  push_string(make_shared_binary_string(data+tmp2, pos-tmp2));
-
-		  SKIPWHITE();
-		  argno++;
-		  if(argno>=MAX_ARGS)
-		  {
-		    cpp_error(this,"Too many arguments in macro definition.");
-		    pop_stack();
-		    argno--;
-		  }
-		}
-
-	      if(!GOBBLE(')'))
-		cpp_error(this,"Missing ) in macro definition.");
-	    }
-
-	  SKIPSPACE();
-
-	  partbase=sp;
-	  init_string_builder(&str, 0);
-	  
-	  while(1)
-	  {
-	    INT32 extra=0;
-
-/*	    fprintf(stderr,"%c",data[pos]);
-	    fflush(stderr); */
-
-	    switch(data[pos++])
-	    {
-	    case '/':
-	      if(data[pos]=='/')
-	      {
-		string_builder_putchar(&str, ' ');
-		FIND_EOL();
-		continue;
-	      }
-	      
-	      if(data[pos]=='*')
-	      {
-		PUTC(' ');
-		SKIPCOMMENT();
-		continue;
-	      }
-	      
-	      string_builder_putchar(&str, '/');
-	      continue;
-	      
-	    case '0': case '1': case '2': case '3':	case '4':
-	    case '5': case '6': case '7': case '8':	case '9':
-	      string_builder_putchar(&str, ((unsigned char *)data)[pos-1]);
-	      while(data[pos]>='0' && data[pos]<='9')
-		string_builder_putchar(&str, ((unsigned char *)data)[pos++]);
-	      continue;
-	      
-	    case '#':
-	      if(GOBBLE('#'))
-	      {
-		extra=DEF_ARG_NOPRESPACE;
-		while(str.s->len && isspace(((unsigned char *)str.s->str)[str.s->len-1]))
-		  str.s->len--;
-		if(!str.s->len && sp-partbase>1)
-		{
-#ifdef PIKE_DEBUG
-		  if(sp[-1].type != T_INT)
-		    fatal("Internal error in CPP\n");
-#endif
-		  sp[-1].u.integer|=DEF_ARG_NOPOSTSPACE;
-		}
-	      }else{
-		extra=DEF_ARG_STRINGIFY;
-	      }
-	      SKIPSPACE();
-	      pos++;
-	      /* fall through */
-	      
-	    default:
-	      if(isidchar(data[pos-1]))
-	      {
-		struct pike_string *s;
-		tmp3=pos-1;
-		while(isidchar(data[pos])) pos++;
-		if(argno>0)
-		{
-		  if((s=binary_findstring(data+tmp3,pos-tmp3)))
-		  {
-		    for(e=0;e<argno;e++)
-		    {
-		      if(argbase[e].u.string == s)
-		      {
-			check_stack(2);
-			push_string(finish_string_builder(&str));
-			init_string_builder(&str, 0);
-			push_int(e | extra);
-			extra=0;
-			break;
-		      }
-		    }
-		    if(e!=argno) continue;
-		  }
-		}
-		string_builder_binary_strcat(&str, data+tmp3, pos-tmp3);
-	      }else{
-		string_builder_putchar(&str, ((unsigned char *)data)[pos-1]);
-	      }
-	      extra=0;
-	      continue;
-	      
-	    case '"':
-	      FIXSTRING(str, 1);
-	      continue;
-	      
-	    case '\'':
-	      tmp3=pos-1;
-	      FIND_END_OF_CHAR();
-	      string_builder_binary_strcat(&str, data+tmp3, pos-tmp3);
-	      continue;
-	      
-	    case '\\':
-	      if(GOBBLE('\n'))
-	      { 
-		this->current_line++;
-		PUTNL();
-	      }
-	      continue;
-	      
-	    case '\n':
-		PUTNL();
-		this->current_line++;
-	    case 0:
-		break;
-	    }
-	    push_string(finish_string_builder(&str));
-	    break;
-	  }
-
-	  if(OUTP())
-	  {
-	    def=alloc_empty_define(make_shared_binary_string(data+namestart,
-						  nameend-namestart),
-				   (sp-partbase)/2);
-	    copy_shared_string(def->first, partbase->u.string);
-	    def->args=argno;
-	    
-	    for(e=0;e<def->num_parts;e++)
-	    {
-#if 1
-	      if(partbase[e*2+1].type != T_INT)
-		fatal("Cpp internal error, expected integer!\n");
-	      
-	      if(partbase[e*2+2].type != T_STRING)
-		fatal("Cpp internal error, expected string!\n");
-#endif
-	      def->parts[e].argument=partbase[e*2+1].u.integer;
-	      copy_shared_string(def->parts[e].postfix,
-				 partbase[e*2+2].u.string);
-	    }
-	    
-#ifdef PIKE_DEBUG
-	    if(def->num_parts==1 &&
-	       (def->parts[0].argument & DEF_ARG_MASK) > MAX_ARGS)
-	      fatal("Internal error in define\n");
-#endif	  
-	    
-	    this->defines=hash_insert(this->defines, & def->link);
-	    
-	  }
-	  pop_n_elems(sp-argbase);
-	  break;
-	}
-      
-      goto unknown_preprocessor_directive;
-      
-    case 'u': /* undefine */
-      if(WGOBBLE("undefine") || WGOBBLE("undef"))
-	{
-	  INT32 tmp;
-	  struct pike_string *s;
-
-	  SKIPSPACE();
-
-	  tmp=pos;
-	  if(!isidchar(data[pos]))
-	    cpp_error(this,"Undefine what?");
-
-	  while(isidchar(data[pos])) pos++;
-
-	  /* #undef some_long_identifier
-	   *        ^                   ^
-	   *        tmp               pos
-	   */
-
-	  if(OUTP())
-	  {
-	    if((s=binary_findstring(data+tmp, pos-tmp)))
-	      undefine(this,s);
-	  }
+static INT32 low_cpp(struct cpp *this, void *data, INT32 len, int shift,
+		     int flags);
 
-	  break;
-	}
-
-      goto unknown_preprocessor_directive;
+#define SHIFT 0
+#include "preprocessor.h"
+#undef SHIFT
 
-    case 'c': /* charset */
-      if (WGOBBLE("charset")) {
-	/* FIXME: Should probably only be allowed in 8bit strings.
-	 *
-	 * FIXME: Should probably only be allowed at top-level (flags == 0).
-	 */
+#define SHIFT 1
+#include "preprocessor.h"
+#undef SHIFT
 
-	INT32 p;
-	struct pike_string *s;
-
-	SKIPSPACE();
-
-	p = pos;
-	while(data[pos] && !isspace(((unsigned char *)data)[pos])) {
-	  pos++;
-	}
+#define SHIFT 2
+#include "preprocessor.h"
+#undef SHIFT
 
-	if (pos != p) {
-	  /* The rest of the string. */
-	  s = begin_shared_string(len - pos);
-	  MEMCPY(s->str, data + pos, len - pos);
-	  push_string(end_shared_string(s));
-
-	  /* The charset name */
-	  s = begin_shared_string(pos - p);
-	  MEMCPY(s->str, data + p, pos - p);
-	  push_string(end_shared_string(s));
-
-	  SAFE_APPLY_MASTER("decode_charset", 2);
-
-	  if (sp[-1].type != T_STRING) {
-	    pop_stack();
-	    cpp_error(this, "Unknown charset.");
-	  }
-
-	  low_cpp(this, sp[-1].u.string->str, sp[-1].u.string->len, flags);
-	  pop_stack();
-
-	  /* FIXME: Is this the correct thing to return? */
-	  return len;
-	} else {
-	  cpp_error(this, "What charset?");
-	}
-	
-	break;
-      }
-      goto unknown_preprocessor_directive;
-
-    case 'p': /* pragma */
-      if(WGOBBLE("pragma"))
-	{
-	  if(OUTP())
-	    STRCAT("#pragma", 7);
-	  else
-	    FIND_EOL();
-	  break;
-	}
-
-    default:
-    unknown_preprocessor_directive:
-      {
-      char buffer[180];
-      int i;
-      for(i=0;i<(long)sizeof(buffer)-1;i++)
-      {
-	if(!isidchar(data[pos])) break;
-	buffer[i]=data[pos++];
-      }
-      buffer[i]=0;
-	
-      cpp_error(this,"Unknown preprocessor directive.");
-      }
-    }
-    }
-  }
-
-  if(flags & CPP_EXPECT_ENDIF)
-    error("End of file while searching for #endif\n");
-
-  return pos;
-}
-
-static INT32 calcC(struct cpp *this,char *data,INT32 len,INT32 pos)
+static INT32 low_cpp(struct cpp *this, void *data, INT32 len, int shift,
+		     int flags)
 {
-  FINDTOK();
-
-/*  DUMPPOS("calcC"); */
-
-  switch(data[pos])
-  {
-  case '(':
-    pos=calc1(this,data,len,pos+1);
-    FINDTOK();
-    if(!GOBBLE(')'))
-      error("Missing ')'\n");
-    break;
-    
-  case '0':
-    if(data[pos+1]=='x' || data[pos+1]=='X')
-    {
-      char *p;
-      push_int(STRTOL(data+pos+2, &p, 16));
-      pos=p-data;
-      break;
-    }
-    
-  case '1': case '2': case '3': case '4':
-  case '5': case '6': case '7': case '8': case '9':
-  {
-    char *p1,*p2;
-    double f;
-    long l;
-    
-    f=my_strtod(data+pos, &p1);
-    l=STRTOL(data+pos, &p2, 0);
-    if(p1 > p2)
-    {
-      push_float(f);
-      pos=p1-data;
-    }else{
-      push_int(l);
-      pos=p2-data;
-    }
-    break;
-  }
-
-  case '\'':
-  {
-    int tmp;
-    READCHAR(tmp);
-    pos++;
-    if(!GOBBLE('\''))
-      error("Missing end quote in character constant.\n");
-    push_int(tmp);
-    break;
-  }
-
-  case '"':
-  {
-    struct string_builder s;
-    init_string_builder(&s, 0);
-    READSTRING(s);
-    push_string(finish_string_builder(&s));
-    break;
-  }
-  
+  switch(shift) {
+  case 0:
+    return lower_cpp0(this, (p_wchar0 *)data, len, flags);
+  case 1:
+    return lower_cpp1(this, (p_wchar1 *)data, len, flags);
+  case 2:
+    return lower_cpp2(this, (p_wchar2 *)data, len, flags);
   default:
-#ifdef PIKE_DEBUG
-    if(isidchar(data[pos]))
-      error("Syntax error in #if (should not happen)\n");
-#endif
-
-    error("Syntax error in #if.\n");
-  }
-  
-
-  FINDTOK();
-
-  while(GOBBLE('['))
-  {
-    pos=calc1(this,data,len,pos);
-    f_index(2);
-
-    FINDTOK();
-    if(!GOBBLE(']'))
-      error("Missing ']'");
-  }
-/*   DUMPPOS("after calcC"); */
-  return pos;
-}
-
-static INT32 calcB(struct cpp *this,char *data,INT32 len,INT32 pos)
-{
-  FINDTOK();
-  switch(data[pos])
-  {
-    case '-': pos++; pos=calcB(this,data,len,pos); o_negate(); break;
-    case '!': pos++; pos=calcB(this,data,len,pos); o_not(); break;
-    case '~': pos++; pos=calcB(this,data,len,pos); o_compl(); break;
-    default: pos=calcC(this,data,len,pos);
-  }
-/*   DUMPPOS("after calcB"); */
-  return pos;
-}
-
-static INT32 calcA(struct cpp *this,char *data,INT32 len,INT32 pos)
-{
-  pos=calcB(this,data,len,pos);
-  while(1)
-  {
-/*     DUMPPOS("inside calcA"); */
-    FINDTOK();
-    switch(data[pos])
-    {
-      case '/':
-	if(data[1]=='/' || data[1]=='*') return pos;
-	pos++;
-	pos=calcB(this,data,len,pos);
-	o_divide();
-	continue;
-
-      case '*':
-	pos++;
-	pos=calcB(this,data,len,pos);
-	o_multiply();
-	continue;
-
-      case '%':
-	pos++;
-	pos=calcB(this,data,len,pos);
-	o_mod();
-	continue;
-    }
-    break;
-  }
-/*   DUMPPOS("after calcA"); */
-  return pos;
-}
-
-static INT32 calc9(struct cpp *this,char *data,INT32 len,INT32 pos)
-{
-  pos=calcA(this,data,len,pos);
-
-  while(1)
-  {
-    FINDTOK();
-    switch(data[pos])
-    {
-      case '+':
-	pos++;
-	pos=calcA(this,data,len,pos);
-	f_add(2);
-	continue;
-
-      case '-':
-	pos++;
-	pos=calcA(this,data,len,pos);
-	o_subtract();
-	continue;
-    }
-    break;
+    fatal("low_cpp(): Bad shift: %d\n", shift);
   }
-
-/*   DUMPPOS("after calc9"); */
-  return pos;
-}
-
-static INT32 calc8(struct cpp *this,char *data,INT32 len,INT32 pos)
-{
-  pos=calc9(this,data,len,pos);
-
-  while(1)
-  {
-    FINDTOK();
-    if(GOBBLEOP("<<"))
-    {
-      pos=calc9(this,data,len,pos);
-      o_lsh();
-      break;
-    }
-
-    if(GOBBLEOP(">>"))
-    {
-      pos=calc9(this,data,len,pos);
-      o_rsh();
-      break;
-    }
-
-    break;
-  }
-  return pos;
-}
-
-static INT32 calc7b(struct cpp *this,char *data,INT32 len,INT32 pos)
-{
-  pos=calc8(this,data,len,pos);
-
-  while(1)
-  {
-    FINDTOK();
-    
-    switch(data[pos])
-    {
-      case '<':
-	if(data[pos]+1 == '<') break;
-	pos++;
-	if(GOBBLE('='))
-	{
-	   pos=calc8(this,data,len,pos);
-	   f_le(2);
-	}else{
-	   pos=calc8(this,data,len,pos);
-	   f_lt(2);
-	}
-	continue;
-
-      case '>':
-	if(data[pos]+1 == '>') break;
-	pos++;
-	if(GOBBLE('='))
-	{
-	   pos=calc8(this,data,len,pos);
-	   f_ge(2);
-	}else{
-	   pos=calc8(this,data,len,pos);
-	   f_gt(2);
-	}
-	continue;
-    }
-    break;
-  }
-  return pos;
-}
-
-static INT32 calc7(struct cpp *this,char *data,INT32 len,INT32 pos)
-{
-  pos=calc7b(this,data,len,pos);
-
-  while(1)
-  {
-    FINDTOK();
-    if(GOBBLEOP("=="))
-    {
-      pos=calc7b(this,data,len,pos);
-      f_eq(2);
-      continue;
-    }
-
-    if(GOBBLEOP("!="))
-    {
-      pos=calc7b(this,data,len,pos);
-      f_ne(2);
-      continue;
-    }
-
-    break;
-  }
-  return pos;
-}
-
-static INT32 calc6(struct cpp *this,char *data,INT32 len,INT32 pos)
-{
-  pos=calc7(this,data,len,pos);
-
-  FINDTOK();
-  while(data[pos] == '&' && data[pos+1]!='&')
-  {
-    pos++;
-    pos=calc7(this,data,len,pos);
-    o_and();
-  }
-  return pos;
-}
-
-static INT32 calc5(struct cpp *this,char *data,INT32 len,INT32 pos)
-{
-  pos=calc6(this,data,len,pos);
-
-  FINDTOK();
-  while(GOBBLE('^'))
-  {
-    pos=calc6(this,data,len,pos);
-    o_xor();
-  }
-  return pos;
-}
-
-static INT32 calc4(struct cpp *this,char *data,INT32 len,INT32 pos)
-{
-  pos=calc5(this,data,len,pos);
-
-  FINDTOK();
-  while(data[pos] == '|' && data[pos+1]!='|')
-  {
-    pos++;
-    pos=calc5(this,data,len,pos);
-    o_or();
-  }
-  return pos;
-}
-
-static INT32 calc3(struct cpp *this,char *data,INT32 len,INT32 pos)
-{
-  pos=calc4(this,data,len,pos);
-
-  FINDTOK();
-  while(GOBBLEOP("&&"))
-  {
-    check_destructed(sp-1);
-    if(IS_ZERO(sp-1))
-    {
-      pos=calc4(this,data,len,pos);
-      pop_stack();
-    }else{
-      pop_stack();
-      pos=calc4(this,data,len,pos);
-    }
-  }
-  return pos;
-}
-
-static INT32 calc2(struct cpp *this,char *data,INT32 len,INT32 pos)
-{
-  pos=calc3(this,data,len,pos);
-
-  FINDTOK();
-  while(GOBBLEOP("||"))
-  {
-    check_destructed(sp-1);
-    if(!IS_ZERO(sp-1))
-    {
-      pos=calc3(this,data,len,pos);
-      pop_stack();
-    }else{
-      pop_stack();
-      pos=calc3(this,data,len,pos);
-    }
-  }
-  return pos;
-}
-
-static INT32 calc1(struct cpp *this,char *data,INT32 len,INT32 pos)
-{
-  pos=calc2(this,data,len,pos);
-
-  FINDTOK();
-
-  if(GOBBLE('?'))
-  {
-    pos=calc1(this,data,len,pos);
-    if(!GOBBLE(':'))
-      error("Colon expected");
-    pos=calc1(this,data,len,pos);
-
-    check_destructed(sp-3);
-    assign_svalue(sp-3,IS_ZERO(sp-3)?sp-1:sp-2);
-    pop_n_elems(2);
-  }
-  return pos;
-}
-
-static int calc(struct cpp *this,char *data,INT32 len,INT32 tmp)
-{
-  JMP_BUF recovery;
-  INT32 pos;
-
-/*  fprintf(stderr,"Calculating\n"); */
-
-  if (SETJMP(recovery))
-  {
-    pos=tmp;
-    if(throw_value.type == T_ARRAY && throw_value.u.array->size)
-    {
-      union anything *a;
-      a=low_array_get_item_ptr(throw_value.u.array, 0, T_STRING);
-      if(a)
-      {
-	cpp_error(this,a->string->str);
-      }else{
-	cpp_error(this,"Nonstandard error format.");
-      }
-    }else{
-      cpp_error(this,"Nonstandard error format.");
-    }
-    FIND_EOL();
-    push_int(0);
-  }else{
-    pos=calc1(this,data,len,tmp);
-    check_destructed(sp-1);
-  }
-  UNSETJMP(recovery);
-
-/*  fprintf(stderr,"Done\n"); */
-
-  return pos;
 }
 
 void free_one_define(struct hash_entry *h)
@@ -1947,7 +520,8 @@ static void insert_current_file_as_string(struct cpp *this,
 					  struct define_argument *args,
 					  struct string_builder *tmp)
 {
-  PUSH_STRING(this->current_file->str, this->current_file->len, tmp);
+  PUSH_STRING_SHIFT(this->current_file->str, this->current_file->len,
+		    this->current_file->size_shift, tmp);
 }
 
 static void insert_current_time_as_string(struct cpp *this,
@@ -1955,12 +529,13 @@ static void insert_current_time_as_string(struct cpp *this,
 					  struct define_argument *args,
 					  struct string_builder *tmp)
 {
+  /* FIXME: Is this code safe? */
   time_t tmp2;
   char *buf;
   time(&tmp2);
   buf=ctime(&tmp2);
 
-  PUSH_STRING(buf+11, 8, tmp);
+  PUSH_STRING0(buf+11, 8, tmp);
 }
 
 static void insert_current_date_as_string(struct cpp *this,
@@ -1968,13 +543,14 @@ static void insert_current_date_as_string(struct cpp *this,
 					  struct define_argument *args,
 					  struct string_builder *tmp)
 {
+  /* FIXME: Is this code safe? */
   time_t tmp2;
   char *buf;
   time(&tmp2);
   buf=ctime(&tmp2);
 
-  PUSH_STRING(buf+19, 5, tmp);
-  PUSH_STRING(buf+4, 6, tmp);
+  PUSH_STRING0(buf+19, 5, tmp);
+  PUSH_STRING0(buf+4, 6, tmp);
 }
 
 static void check_defined(struct cpp *this,
@@ -1983,7 +559,20 @@ static void check_defined(struct cpp *this,
 			  struct string_builder *tmp)
 {
   struct pike_string *s;
-  s=binary_findstring(args[0].arg, args[0].len);
+  switch(args[0].arg.shift) {
+  case 0:
+    s=binary_findstring((p_wchar0 *)args[0].arg.ptr, args[0].len);
+    break;
+  case 1:
+    s=binary_findstring1((p_wchar1 *)args[0].arg.ptr, args[0].len);
+    break;
+  case 2:
+    s=binary_findstring2((p_wchar2 *)args[0].arg.ptr, args[0].len);
+    break;
+  default:
+    fatal("cpp(): Symbol has unsupported shift: %d\n", args[0].arg.shift);
+    break;
+  }
   if(s && find_define(s))
   {
     string_builder_binary_strcat(tmp, " 1 ", 3);
@@ -1999,12 +588,27 @@ static void dumpdef(struct cpp *this,
 {
   struct pike_string *s;
   struct define *d;
-  s=binary_findstring(args[0].arg, args[0].len);
+
+  switch(args[0].arg.shift) {
+  case 0:
+    s=binary_findstring((p_wchar0 *)args[0].arg.ptr, args[0].len);
+    break;
+  case 1:
+    s=binary_findstring1((p_wchar1 *)args[0].arg.ptr, args[0].len);
+    break;
+  case 2:
+    s=binary_findstring2((p_wchar2 *)args[0].arg.ptr, args[0].len);
+    break;
+  default:
+    fatal("cpp(): Bad shift in macroname: %d\n", args[0].arg.shift);
+    break;
+  }
   if(s && (d=find_define(s)))
   {
     int e;
     char buffer[42];
-    PUSH_STRING(d->link.s->str,d->link.s->len, tmp);
+    PUSH_STRING_SHIFT(d->link.s->str, d->link.s->len,
+		      d->link.s->size_shift, tmp);
     if(d->magic)
     {
       string_builder_binary_strcat(tmp, " defined magically ", 19);
@@ -2012,7 +616,8 @@ static void dumpdef(struct cpp *this,
       string_builder_binary_strcat(tmp, " defined as ", 12);
       
       if(d->first)
-	PUSH_STRING(d->first->str, d->first->len, tmp);
+	PUSH_STRING_SHIFT(d->first->str, d->first->len,
+			  d->first->size_shift, tmp);
       for(e=0;e<d->num_parts;e++)
       {
 	if(!(d->parts[e].argument & DEF_ARG_NOPRESPACE))
@@ -2027,7 +632,8 @@ static void dumpdef(struct cpp *this,
 	if(!(d->parts[e].argument & DEF_ARG_NOPOSTSPACE))
 	  string_builder_putchar(tmp, ' ');
 	
-	PUSH_STRING(d->parts[e].postfix->str, d->parts[e].postfix->len, tmp);
+	PUSH_STRING_SHIFT(d->parts[e].postfix->str, d->parts[e].postfix->len,
+			  d->parts[e].postfix->size_shift, tmp);
       } 
     }
   }else{
@@ -2044,19 +650,29 @@ static void check_constant(struct cpp *this,
 {
   struct svalue *save_stack=sp;
   struct svalue *sv;
-  char *data=args[0].arg;
+  PCHARP data=args[0].arg;
   int res,dlen,len=args[0].len;
+  struct pike_string *s;
+  int c;
 
-  while(len && isspace(((unsigned char *)data)[0])) { data++; len--; }
+  while(len && ((c = EXTRACT_PCHARP(data))< 256) && isspace(c)) {
+    INC_PCHARP(data, 1);
+    len--;
+  }
 
   if(!len)
     cpp_error(this,"#if constant() with empty argument.\n");
 
   for(dlen=0;dlen<len;dlen++)
-    if(!isidchar(data[dlen]))
+    if(!isidchar(INDEX_PCHARP(data, dlen)))
       break;
 
-  push_string(make_shared_binary_string(data, dlen));
+  s = begin_wide_shared_string(dlen, data.shift);
+  MEMCPY(s->str, data.ptr, dlen<<data.shift);
+  push_string(end_shared_string(s));
+#ifdef PIKE_DEBUG
+  s = NULL;
+#endif /* PIKE_DEBUG */
   if((sv=low_mapping_string_lookup(get_builtin_constants(),
 				   sp[-1].u.string)))
   {
@@ -2075,27 +691,35 @@ static void check_constant(struct cpp *this,
 
   while(1)
   {
-    data+=dlen;
+    INC_PCHARP(data, dlen);
     len-=dlen;
   
-    while(len && isspace(((unsigned char *)data)[0])) { data++; len--; }
+    while(len && ((c = EXTRACT_PCHARP(data)) < 256) && isspace(c)) {
+      INC_PCHARP(data, 1);
+      len--;
+    }
 
     if(!len) break;
 
-    if(data[0]=='.')
+    if(EXTRACT_PCHARP(data) == '.')
     {
-      data++;
+      INC_PCHARP(data, 1);
       len--;
       
-      while(len && isspace(((unsigned char *)data)[0])) { data++; len--; }
+      while(len && ((c = EXTRACT_PCHARP(data)) < 256) && isspace(c)) {
+	INC_PCHARP(data, 1);
+	len--;
+      }
 
-      for(dlen=0;dlen<len;dlen++)
-	if(!isidchar(data[dlen]))
+      for(dlen=0; dlen<len; dlen++)
+	if(!isidchar(INDEX_PCHARP(data, dlen)))
 	  break;
 
       if(res)
       {
-        struct pike_string *s=make_shared_binary_string(data, dlen);
+        struct pike_string *s = begin_wide_shared_string(dlen, data.shift);
+	MEMCPY(s->str, data.ptr, dlen<<data.shift);
+	s = end_shared_string(s);
 	res=do_safe_index_call(s);
         free_string(s);
       }
@@ -2495,10 +1119,11 @@ void f_cpp(INT32 args)
     simple_add_define(&this, tmpf->name, tmpf->value);
 
   string_builder_binary_strcat(&this.buf, "# 1 ", 4);
-  PUSH_STRING(this.current_file->str, this.current_file->len, &this.buf);
+  PUSH_STRING_SHIFT(this.current_file->str, this.current_file->len,
+		    this.current_file->size_shift, &this.buf);
   string_builder_putchar(&this.buf, '\n');
 
-  low_cpp(&this, data->str, data->len, 0);
+  low_cpp(&this, data->str, data->len, data->size_shift, 0);
   if(this.defines)
     free_hashtable(this.defines, free_one_define);
 
diff --git a/src/preprocessor.h b/src/preprocessor.h
new file mode 100644
index 0000000000000000000000000000000000000000..e7cfc2dedc15b55963c5c9f9deb768819dc7d002
--- /dev/null
+++ b/src/preprocessor.h
@@ -0,0 +1,1818 @@
+/*
+ * $Id: preprocessor.h,v 1.1 1999/02/27 21:52:38 grubba Exp $
+ *
+ * Preprocessor template.
+ * Based on cpp.c 1.45
+ *
+ * 1999-02-27 Henrik Grubbström
+ */
+
+#ifndef SHIFT
+#error Internal error: SHIFT not defined
+#endif
+
+/*
+ * Definitions
+ */
+
+#if (SHIFT == 0)
+
+#define WCHAR	p_wchar0
+
+#define WC_ISSPACE	isspace
+#define WC_ISIDCHAR	isidchar
+
+/*
+ * Function renaming
+ */
+
+#define lower_cpp		lower_cpp0
+#define find_end_parenthesis	find_end_parenthesis0
+#define PUSH_STRING		PUSH_STRING0
+#define WC_BINARY_FINDSTRING	binary_findstring
+
+#define calc	calc_0
+#define calc1	calc1_0
+#define calc2	calc2_0
+#define calc3	calc3_0
+#define calc4	calc4_0
+#define calc5	calc5_0
+#define calc6	calc6_0
+#define calc7	calc7_0
+#define calc7b	calc7b_0
+#define calc8	calc8_0
+#define calc9	calc9_0
+#define calcA	calcA_0
+#define calcB	calcB_0
+#define calcC	calcC_0
+
+#else /* SHIFT != 0 */
+
+#define WC_ISSPACE(C)	(((C) < 256)?isspace(C):0)
+#define WC_ISIDCHAR(C)	(((C) < 256)?isidchar(C):1)
+
+#if (SHIFT == 1)
+
+#define WCHAR	p_wchar1
+
+
+/*
+ * Function renaming
+ */
+
+#define lower_cpp		lower_cpp1
+#define find_end_parenthesis	find_end_parenthesis1
+#define PUSH_STRING		PUSH_STRING1
+#define WC_BINARY_FINDSTRING	binary_findstring1
+
+#define calc	calc_1
+#define calc1	calc1_1
+#define calc2	calc2_1
+#define calc3	calc3_1
+#define calc4	calc4_1
+#define calc5	calc5_1
+#define calc6	calc6_1
+#define calc7	calc7_1
+#define calc7b	calc7b_1
+#define calc8	calc8_1
+#define calc9	calc9_1
+#define calcA	calcA_1
+#define calcB	calcB_1
+#define calcC	calcC_1
+
+#else /* SHIFT != 1 */
+
+#define WCHAR	p_wchar2
+
+/*
+ * Function renaming
+ */
+
+#define lower_cpp		lower_cpp2
+#define find_end_parenthesis	find_end_parenthesis2
+#define PUSH_STRING		PUSH_STRING2
+#define WC_BINARY_FINDSTRING	binary_findstring2
+
+#define calc	calc_2
+#define calc1	calc1_2
+#define calc2	calc2_2
+#define calc3	calc3_2
+#define calc4	calc4_2
+#define calc5	calc5_2
+#define calc6	calc6_2
+#define calc7	calc7_2
+#define calc7b	calc7b_2
+#define calc8	calc8_2
+#define calc9	calc9_2
+#define calcA	calcA_2
+#define calcB	calcB_2
+#define calcC	calcC_2
+
+#endif /* SHIFT == 1 */
+
+
+static struct pike_string *WC_BINARY_FINDSTRING(WCHAR *str, INT32 len)
+{
+  struct pike_string *s;
+#if (SHIFT == 1)
+  s = make_shared_binary_string1(str, len);
+#else /* SHIFT != 1 */
+#if (SHIFT == 2)
+  s = make_shared_binary_string2(str, len);
+#else /* SHIFT != 2 */
+#error Bad SHIFT
+#endif /* SHIFT == 2 */
+#endif /* SHIFT == 1 */
+
+  if (s->refs == 1) {
+    free_string(s);
+    return NULL;
+  }
+  free_string(s);
+  return(s);
+}
+
+#endif /* SHIFT == 0 */
+
+/*
+ * Generic macros
+ */
+#define STRCAT(STR,LEN) do {				\
+  INT32 x_,len_=(LEN);					\
+  char *str_=(STR);					\
+  if(OUTP())						\
+    string_builder_binary_strcat(&this->buf, str_, len_);	\
+  else							\
+    for(x_=0;x_<len_;x_++)				\
+      if(str_[x_]=='\n')				\
+        string_builder_putchar(&this->buf, '\n');	\
+}while(0)
+
+#define WC_STRCAT(STR,LEN) do {				\
+  INT32 x_,len_=(LEN);					\
+  WCHAR *str_=(STR);					\
+  if(OUTP())						\
+    string_builder_append(&this->buf, MKPCHARP(str_, SHIFT), len_);	\
+  else							\
+    for(x_=0;x_<len_;x_++)				\
+      if(str_[x_]=='\n')				\
+        string_builder_putchar(&this->buf, '\n');	\
+}while(0)
+
+#if 0	/* OBSOLETE */
+#define CHECKWORD(X) \
+ (!strncmp(X,data+pos,strlen(X)) && !WC_ISIDCHAR(data[pos+strlen(X)]))
+#define WGOBBLE(X) (CHECKWORD(X) ? (pos+=strlen(X)),1 : 0)
+#define GOBBLEOP(X) \
+ ((!strncmp(X,data+pos,strlen(X))) ? (pos+=strlen(X)),1 : 0)
+#endif /* 0 */
+
+#define CHECKWORD2(X,LEN) \
+ (!MEMCMP(X,data+pos,LEN<<SHIFT) && !WC_ISIDCHAR(data[pos+LEN]))
+#define WGOBBLE2(X,LEN) (CHECKWORD2(X,LEN) ? (pos+=LEN),1 : 0)
+#define GOBBLEOP2(X,LEN) \
+ ((!MEMCMP(X,data+pos,LEN)) ? (pos += LEN),1 : 0)
+
+/*
+ * Some prototypes
+ */
+
+static void PUSH_STRING0(p_wchar0 *, INT32, struct string_builder *);
+static void PUSH_STRING1(p_wchar1 *, INT32, struct string_builder *);
+static void PUSH_STRING2(p_wchar2 *, INT32, struct string_builder *);
+
+static INT32 calc(struct cpp *,WCHAR *,INT32,INT32);
+static INT32 calc1(struct cpp *,WCHAR *,INT32,INT32);
+
+/*
+ * Functions
+ */
+
+#if (SHIFT == 0)
+static void PUSH_STRING_SHIFT(void *str, INT32 len, int shift,
+			      struct string_builder *buf)
+{
+  switch(shift) {
+  case 0:
+    PUSH_STRING0((p_wchar0 *)str, len, buf);
+    break;
+  case 1:
+    PUSH_STRING1((p_wchar1 *)str, len, buf);
+    break;
+  case 2:
+    PUSH_STRING2((p_wchar2 *)str, len, buf);
+    break;
+  default:
+    fatal("cpp(): Bad string shift detected in PUSH_STRING_SHIFT(): %d\n",
+	  shift);
+    break;
+  }
+}
+#endif /* SHIFT == 0 */
+
+static void PUSH_STRING(WCHAR *str,
+			INT32 len,
+			struct string_builder *buf)
+{
+  INT32 p2;
+  string_builder_putchar(buf, '"');
+  for(p2=0; p2<len; p2++)
+  {
+    switch(str[p2])
+    {
+    case '\n':
+      string_builder_putchar(buf, '\\');
+      string_builder_putchar(buf, 'n');
+      break;
+      
+    case '\t':
+      string_builder_putchar(buf, '\\');
+      string_builder_putchar(buf, 't');
+      break;
+      
+    case '\r':
+      string_builder_putchar(buf, '\\');
+      string_builder_putchar(buf, 'r');
+      break;
+      
+    case '\b':
+      string_builder_putchar(buf, '\\');
+      string_builder_putchar(buf, 'b');
+      break;
+      
+    case '\\':
+    case '"':
+      string_builder_putchar(buf, '\\');
+      string_builder_putchar(buf, str[p2]);
+      break;
+      
+    default:
+      if(isprint(EXTRACT_UCHAR(str+p2)))
+      {
+	string_builder_putchar(buf, str[p2]);
+      }
+      else
+      {
+	int c = str[p2];
+#if (SHIFT != 0)
+	if (c < 256) {
+#endif /* SHIFT != 0 */
+	  string_builder_putchar(buf, '\\');
+	  string_builder_putchar(buf, ((c>>6)&7)+'0');
+	  string_builder_putchar(buf, ((c>>3)&7)+'0');
+	  string_builder_putchar(buf, (c&7)+'0');
+	  if(EXTRACT_UCHAR(str+p2+1)>='0' && EXTRACT_UCHAR(str+p2+1)<='7')
+	  {
+	    string_builder_putchar(buf, '"');
+	    string_builder_putchar(buf, '"');
+	  }
+#if (SHIFT != 0)
+	} else {
+	  string_builder_putchar(buf, c);
+	}
+#endif /* SHIFT != 0 */
+      }
+      break;
+    }
+  }
+  string_builder_putchar(buf, '"');
+}
+
+static INLINE int find_end_parenthesis(struct cpp *this,
+				       WCHAR *data,
+				       INT32 len,
+				       INT32 pos) /* position of first " */
+{
+  while(1)
+  {
+    if(pos+1>=len)
+    {
+      cpp_error(this,"End of file while looking for end parenthesis.");
+      return pos;
+    }
+
+    switch(data[pos++])
+    {
+    case '\n': PUTNL(); this->current_line++; break;
+    case '\'': FIND_END_OF_CHAR();  break;
+    case '"':  FIND_END_OF_STRING();  break;
+    case '(':  pos=find_end_parenthesis(this, data, len, pos); break;
+    case ')':  return pos;
+    }
+  }
+}
+
+static INT32 calcC(struct cpp *this,WCHAR *data,INT32 len,INT32 pos)
+{
+  FINDTOK();
+
+/*  DUMPPOS("calcC"); */
+
+  switch(data[pos])
+  {
+  case '(':
+    pos=calc1(this,data,len,pos+1);
+    FINDTOK();
+    if(!GOBBLE(')'))
+      error("Missing ')'\n");
+    break;
+    
+  case '0':
+    if(data[pos+1]=='x' || data[pos+1]=='X')
+    {
+      PCHARP p;
+      push_int(STRTOL_PCHARP(MKPCHARP(data+pos+2, SHIFT), &p, 16));
+      pos = ((WCHAR *)p.ptr) - data;
+      break;
+    }
+    
+  case '1': case '2': case '3': case '4':
+  case '5': case '6': case '7': case '8': case '9':
+  {
+    PCHARP p1,p2;
+    PCHARP p;
+    double f;
+    long l;
+    
+    p = MKPCHARP(data+pos, SHIFT);
+    f = strtod_pcharp(p, &p1);
+    l = STRTOL_PCHARP(p, &p2, 0);
+    if(p1.ptr > p2.ptr)
+    {
+      push_float(f);
+      pos = ((WCHAR *)p1.ptr) - data;
+    }else{
+      push_int(l);
+      pos = ((WCHAR *)p2.ptr) - data;
+    }
+    break;
+  }
+
+  case '\'':
+  {
+    int tmp;
+    READCHAR(tmp);
+    pos++;
+    if(!GOBBLE('\''))
+      error("Missing end quote in character constant.\n");
+    push_int(tmp);
+    break;
+  }
+
+  case '"':
+  {
+    struct string_builder s;
+    init_string_builder(&s, 0);
+    READSTRING(s);
+    push_string(finish_string_builder(&s));
+    break;
+  }
+  
+  default:
+#ifdef PIKE_DEBUG
+    if(WC_ISIDCHAR(data[pos]))
+      error("Syntax error in #if (should not happen)\n");
+#endif
+
+    error("Syntax error in #if.\n");
+  }
+  
+
+  FINDTOK();
+
+  while(GOBBLE('['))
+  {
+    pos=calc1(this,data,len,pos);
+    f_index(2);
+
+    FINDTOK();
+    if(!GOBBLE(']'))
+      error("Missing ']'");
+  }
+/*   DUMPPOS("after calcC"); */
+  return pos;
+}
+
+static INT32 calcB(struct cpp *this,WCHAR *data,INT32 len,INT32 pos)
+{
+  FINDTOK();
+  switch(data[pos])
+  {
+    case '-': pos++; pos=calcB(this,data,len,pos); o_negate(); break;
+    case '!': pos++; pos=calcB(this,data,len,pos); o_not(); break;
+    case '~': pos++; pos=calcB(this,data,len,pos); o_compl(); break;
+    default: pos=calcC(this,data,len,pos);
+  }
+/*   DUMPPOS("after calcB"); */
+  return pos;
+}
+
+static INT32 calcA(struct cpp *this,WCHAR *data,INT32 len,INT32 pos)
+{
+  pos=calcB(this,data,len,pos);
+  while(1)
+  {
+/*     DUMPPOS("inside calcA"); */
+    FINDTOK();
+    switch(data[pos])
+    {
+      case '/':
+	if(data[1]=='/' || data[1]=='*') return pos;
+	pos++;
+	pos=calcB(this,data,len,pos);
+	o_divide();
+	continue;
+
+      case '*':
+	pos++;
+	pos=calcB(this,data,len,pos);
+	o_multiply();
+	continue;
+
+      case '%':
+	pos++;
+	pos=calcB(this,data,len,pos);
+	o_mod();
+	continue;
+    }
+    break;
+  }
+/*   DUMPPOS("after calcA"); */
+  return pos;
+}
+
+static INT32 calc9(struct cpp *this,WCHAR *data,INT32 len,INT32 pos)
+{
+  pos=calcA(this,data,len,pos);
+
+  while(1)
+  {
+    FINDTOK();
+    switch(data[pos])
+    {
+      case '+':
+	pos++;
+	pos=calcA(this,data,len,pos);
+	f_add(2);
+	continue;
+
+      case '-':
+	pos++;
+	pos=calcA(this,data,len,pos);
+	o_subtract();
+	continue;
+    }
+    break;
+  }
+
+/*   DUMPPOS("after calc9"); */
+  return pos;
+}
+
+static INT32 calc8(struct cpp *this,WCHAR *data,INT32 len,INT32 pos)
+{
+  pos=calc9(this,data,len,pos);
+
+  while(1)
+  {
+    static WCHAR lsh_[] = { '<', '<' };
+    static WCHAR rsh_[] = { '>', '>' };
+
+    FINDTOK();
+    if(GOBBLEOP2(lsh_, 2))
+    {
+      pos=calc9(this,data,len,pos);
+      o_lsh();
+      break;
+    }
+
+    if(GOBBLEOP2(rsh_, 2))
+    {
+      pos=calc9(this,data,len,pos);
+      o_rsh();
+      break;
+    }
+
+    break;
+  }
+  return pos;
+}
+
+static INT32 calc7b(struct cpp *this,WCHAR *data,INT32 len,INT32 pos)
+{
+  pos=calc8(this,data,len,pos);
+
+  while(1)
+  {
+    FINDTOK();
+    
+    switch(data[pos])
+    {
+      case '<':
+	if(data[pos]+1 == '<') break;
+	pos++;
+	if(GOBBLE('='))
+	{
+	   pos=calc8(this,data,len,pos);
+	   f_le(2);
+	}else{
+	   pos=calc8(this,data,len,pos);
+	   f_lt(2);
+	}
+	continue;
+
+      case '>':
+	if(data[pos]+1 == '>') break;
+	pos++;
+	if(GOBBLE('='))
+	{
+	   pos=calc8(this,data,len,pos);
+	   f_ge(2);
+	}else{
+	   pos=calc8(this,data,len,pos);
+	   f_gt(2);
+	}
+	continue;
+    }
+    break;
+  }
+  return pos;
+}
+
+static INT32 calc7(struct cpp *this,WCHAR *data,INT32 len,INT32 pos)
+{
+  pos=calc7b(this,data,len,pos);
+
+  while(1)
+  {
+    static WCHAR eq_[] = { '=', '=' };
+    static WCHAR ne_[] = { '!', '=' };
+
+    FINDTOK();
+    if(GOBBLEOP2(eq_, 2))
+    {
+      pos=calc7b(this,data,len,pos);
+      f_eq(2);
+      continue;
+    }
+
+    if(GOBBLEOP2(ne_, 2))
+    {
+      pos=calc7b(this,data,len,pos);
+      f_ne(2);
+      continue;
+    }
+
+    break;
+  }
+  return pos;
+}
+
+static INT32 calc6(struct cpp *this,WCHAR *data,INT32 len,INT32 pos)
+{
+  pos=calc7(this,data,len,pos);
+
+  FINDTOK();
+  while(data[pos] == '&' && data[pos+1]!='&')
+  {
+    pos++;
+    pos=calc7(this,data,len,pos);
+    o_and();
+  }
+  return pos;
+}
+
+static INT32 calc5(struct cpp *this,WCHAR *data,INT32 len,INT32 pos)
+{
+  pos=calc6(this,data,len,pos);
+
+  FINDTOK();
+  while(GOBBLE('^'))
+  {
+    pos=calc6(this,data,len,pos);
+    o_xor();
+  }
+  return pos;
+}
+
+static INT32 calc4(struct cpp *this,WCHAR *data,INT32 len,INT32 pos)
+{
+  pos=calc5(this,data,len,pos);
+
+  FINDTOK();
+  while(data[pos] == '|' && data[pos+1]!='|')
+  {
+    pos++;
+    pos=calc5(this,data,len,pos);
+    o_or();
+  }
+  return pos;
+}
+
+static INT32 calc3(struct cpp *this,WCHAR *data,INT32 len,INT32 pos)
+{
+  static WCHAR land_[] = { '&', '&' };
+
+  pos=calc4(this,data,len,pos);
+
+  FINDTOK();
+  while(GOBBLEOP2(land_, 2))
+  {
+    check_destructed(sp-1);
+    if(IS_ZERO(sp-1))
+    {
+      pos=calc4(this,data,len,pos);
+      pop_stack();
+    }else{
+      pop_stack();
+      pos=calc4(this,data,len,pos);
+    }
+  }
+  return pos;
+}
+
+static INT32 calc2(struct cpp *this,WCHAR *data,INT32 len,INT32 pos)
+{
+  static WCHAR lor_[] = { '|', '|' };
+
+  pos=calc3(this,data,len,pos);
+
+  FINDTOK();
+  while(GOBBLEOP2(lor_, 2))
+  {
+    check_destructed(sp-1);
+    if(!IS_ZERO(sp-1))
+    {
+      pos=calc3(this,data,len,pos);
+      pop_stack();
+    }else{
+      pop_stack();
+      pos=calc3(this,data,len,pos);
+    }
+  }
+  return pos;
+}
+
+static INT32 calc1(struct cpp *this,WCHAR *data,INT32 len,INT32 pos)
+{
+  pos=calc2(this,data,len,pos);
+
+  FINDTOK();
+
+  if(GOBBLE('?'))
+  {
+    pos=calc1(this,data,len,pos);
+    if(!GOBBLE(':'))
+      error("Colon expected");
+    pos=calc1(this,data,len,pos);
+
+    check_destructed(sp-3);
+    assign_svalue(sp-3,IS_ZERO(sp-3)?sp-1:sp-2);
+    pop_n_elems(2);
+  }
+  return pos;
+}
+
+static int calc(struct cpp *this,WCHAR *data,INT32 len,INT32 tmp)
+{
+  JMP_BUF recovery;
+  INT32 pos;
+
+/*  fprintf(stderr,"Calculating\n"); */
+
+  if (SETJMP(recovery))
+  {
+    pos=tmp;
+    if(throw_value.type == T_ARRAY && throw_value.u.array->size)
+    {
+      union anything *a;
+      a=low_array_get_item_ptr(throw_value.u.array, 0, T_STRING);
+      if(a)
+      {
+	cpp_error(this,a->string->str);
+      }else{
+	cpp_error(this,"Nonstandard error format.");
+      }
+    }else{
+      cpp_error(this,"Nonstandard error format.");
+    }
+    FIND_EOL();
+    push_int(0);
+  }else{
+    pos=calc1(this,data,len,tmp);
+    check_destructed(sp-1);
+  }
+  UNSETJMP(recovery);
+
+/*  fprintf(stderr,"Done\n"); */
+
+  return pos;
+}
+
+static INT32 lower_cpp(struct cpp *this,
+		       WCHAR *data,
+		       INT32 len,
+		       int flags)
+{
+  INT32 pos, tmp, e, tmp2;
+  
+  for(pos=0; pos<len;)
+  {
+/*    fprintf(stderr,"%c",data[pos]);
+    fflush(stderr); */
+
+    switch(data[pos++])
+    {
+    case '\n':
+      if(flags & CPP_END_AT_NEWLINE) return pos-1;
+
+/*      fprintf(stderr,"CURRENT LINE: %d\n",this->current_line); */
+      this->current_line++;
+      PUTNL();
+      goto do_skipwhite;
+
+    case '\t':
+    case ' ':
+    case '\r':
+      PUTC(' ');
+      
+    do_skipwhite:
+      while(data[pos]==' ' || data[pos]=='\r' || data[pos]=='\t')
+	pos++;
+      break;
+
+      /* Minor optimization */
+      case '<':
+	if(data[pos]=='<'  &&
+	   data[pos+1]=='<'  &&
+	   data[pos+2]=='<'  &&
+	   data[pos+3]=='<'  &&
+	   data[pos+4]=='<'  &&
+	   data[pos+5]=='<')
+	  cpp_error(this,"CVS conflict detected");
+	
+      case '!': case '@': case '$': case '%': case '^': case '&':
+      case '*': case '(': case ')': case '-': case '=': case '+':
+      case '{': case '}': case ':': case '?': case '`': case ';':
+      case '>': case ',': case '.': case '~': case '[':
+      case ']': case '|':
+	PUTC(data[pos-1]);
+      break;
+      
+    default:
+      if(OUTP() && WC_ISIDCHAR(data[pos-1]))
+      {
+	struct pike_string *s=0;
+	struct define *d=0;
+	tmp=pos-1;
+	while(WC_ISIDCHAR(data[pos])) pos++;
+
+	if(flags & CPP_DO_IF)
+	{
+	  static PCHARP defined_ = { (p_wchar0 *)"defined", 0 };
+	  static PCHARP efun_ = { (p_wchar0 *)"efun", 0 };
+	  static PCHARP constant_ = { (p_wchar0 *)"constant", 0 };
+	  PCHARP op = MKPCHARP(data + tmp, SHIFT);
+
+	  if(pos-tmp == 7 && !pcharp_memcmp(defined_, op, 7))
+	  {
+	    d=defined_macro;
+	  }
+	  else if((pos-tmp == 4 && !pcharp_memcmp(efun_, op, 4)) ||
+		  (pos-tmp == 8 && !pcharp_memcmp(constant_, op,8)))
+	  {
+	    d=constant_macro;
+	  }
+	  else
+	  {
+	    goto do_find_define;
+	  }
+	}else{
+	do_find_define:
+	  if((s=WC_BINARY_FINDSTRING(data+tmp, pos-tmp)))
+	  {
+	    d=find_define(s);
+	  }
+	}
+	  
+	if(d && !d->inside)
+	{
+	  int arg=0;
+	  struct string_builder tmp;
+	  struct define_argument arguments [MAX_ARGS];
+	  
+	  if(s) add_ref(s);
+	  
+	  if(d->args>=0)
+	  {
+	    SKIPWHITE();
+	    
+	    if(!GOBBLE('('))
+	    {
+	      cpp_error(this,"Missing '(' in macro call.");
+	      break;
+	    }
+	    
+	    for(arg=0;arg<d->args;arg++)
+	    {
+	      if(arg && data[pos]==',')
+	      {
+		pos++;
+		SKIPWHITE();
+	      }else{
+		SKIPWHITE();
+		if(data[pos]==')')
+		{
+		  char buffer[1024];
+		  sprintf(buffer,
+			  "Too few arguments to macro %950s, expected %d.",
+			  d->link.s->str, d->args);
+		  cpp_error(this,buffer);
+		  break;
+		}
+	      }
+	      arguments[arg].arg = MKPCHARP(data + pos, SHIFT);
+
+	      
+	      while(1)
+	      {
+		if(pos+1>len)
+		{
+		  cpp_error(this,"End of file in macro call.");
+		  break;
+		}
+		
+		switch(data[pos++])
+		{
+		case '\n':
+		  this->current_line++;
+		  PUTNL();
+		default: continue;
+		  
+		case '"':
+		  FIND_END_OF_STRING();
+		  continue;
+		  
+		case '\'':
+		  FIND_END_OF_CHAR();
+		  continue;
+		  
+		case '(':
+		  pos=find_end_parenthesis(this, data, len, pos);
+		  continue;
+		  
+		case ')': 
+		case ',': pos--;
+		  break;
+		}
+		break;
+	      }
+	      arguments[arg].len = data + pos -
+		((WCHAR *)(arguments[arg].arg.ptr));
+	    }
+	    SKIPWHITE();
+	    if(!GOBBLE(')'))
+	      cpp_error(this,"Missing ) in macro call.");
+	  }
+	  
+	  if(d->args >= 0 && arg != d->args)
+	    cpp_error(this,"Wrong number of arguments to macro.");
+	  
+	  init_string_builder(&tmp, 0);
+	  if(d->magic)
+	  {
+	    d->magic(this, d, arguments, &tmp);
+	  }else{
+	    string_builder_binary_strcat(&tmp, d->first->str, d->first->len);
+	    for(e=0;e<d->num_parts;e++)
+	    {
+	      WCHAR *a;
+	      INT32 l;
+	      
+	      if((d->parts[e].argument & DEF_ARG_MASK) < 0 || 
+		 (d->parts[e].argument & DEF_ARG_MASK) >= arg)
+	      {
+		cpp_error(this,"Macro not expanded correctly.");
+		continue;
+	      }
+	      
+#ifdef PIKE_DEBUG
+	      if (arguments[d->parts[e].argument&DEF_ARG_MASK].arg.shift !=
+		  SHIFT) {
+		fatal("cpp(): Bad argument shift %d != %d\n", SHIFT,
+		      arguments[d->parts[e].argument&DEF_ARG_MASK].arg.shift);
+	      }
+#endif /* PIKE_DEBUG */
+	      a = (WCHAR *)
+		(arguments[d->parts[e].argument&DEF_ARG_MASK].arg.ptr);
+	      l = arguments[d->parts[e].argument&DEF_ARG_MASK].len;
+	      
+	      if(!(d->parts[e].argument & DEF_ARG_NOPRESPACE))
+		string_builder_putchar(&tmp, ' ');
+	      
+	      if(d->parts[e].argument & DEF_ARG_STRINGIFY)
+	      {
+		PUSH_STRING(a,l,&tmp);
+	      }else{
+		if(DEF_ARG_NOPRESPACE)
+		  while(l && WC_ISSPACE(*a))
+		    a++,l--;
+		
+		if(DEF_ARG_NOPOSTSPACE)
+		  while(l && WC_ISSPACE(a[l-1]))
+		    l--;
+
+		if(d->parts[e].argument & (DEF_ARG_NOPRESPACE | DEF_ARG_NOPOSTSPACE))
+		{
+		  string_builder_append(&tmp, MKPCHARP(a, SHIFT), l);
+		}else{
+		  struct string_builder save;
+		  INT32 line=this->current_line;
+		  save=this->buf;
+		  this->buf=tmp;
+		  lower_cpp(this, a, l,
+			    flags & ~(CPP_EXPECT_ENDIF | CPP_EXPECT_ELSE));
+		  tmp=this->buf;
+		  this->buf=save;
+		  this->current_line=line;
+		}
+	      }
+	      
+	      if(!(d->parts[e].argument & DEF_ARG_NOPOSTSPACE))
+		string_builder_putchar(&tmp, ' ');
+	      
+	      string_builder_binary_strcat(&tmp, d->parts[e].postfix->str,
+					   d->parts[e].postfix->len);
+	    }
+	  }
+	  
+	  /* FIXME */
+	  for(e=0;e<(long)tmp.s->len;e++)
+	    if(tmp.s->str[e]=='\n')
+	      tmp.s->str[e]=' ';
+
+	  if(s) d->inside=1;
+	  
+	  string_builder_putchar(&tmp, 0);
+	  tmp.s->len--;
+#ifdef PIKE_DEBUG
+	  if (tmp.s->size_shift != SHIFT) {
+	    fatal("cpp(): Bad shift in #if: %d != %d\n",
+		  tmp.s->size_shift, SHIFT);
+	  }
+#endif /* PIKE_DEBUG */
+	  lower_cpp(this, (WCHAR *)tmp.s->str, tmp.s->len, 
+		    flags & ~(CPP_EXPECT_ENDIF | CPP_EXPECT_ELSE));
+	  
+	  if(s)
+	  {
+	    if((d=find_define(s)))
+	      d->inside=0;
+	    
+	    free_string(s);
+	  }
+
+	  free_string_builder(&tmp);
+	  break;
+	}else{
+	  if(flags & CPP_DO_IF)
+	  {
+	    STRCAT(" 0 ", 3);
+	  }else{
+	    WC_STRCAT(data+tmp, pos-tmp);
+	  }
+	}
+      }else{
+	PUTC(data[pos-1]);
+      }
+      break;
+      
+    case '0': case '1': case '2': case '3': case '4':
+    case '5': case '6': case '7': case '8': case '9':
+      PUTC(data[pos-1]);
+      while(data[pos]>='0' && data[pos]<='9') PUTC(data[pos++]);
+      break;
+
+    case '"':
+      FIXSTRING(this->buf,OUTP());
+      break;
+
+    case '\'':
+      tmp=pos-1;
+      FIND_END_OF_CHAR();
+      WC_STRCAT(data+tmp, pos-tmp);
+      break;
+
+    case '/':
+      if(data[pos]=='/')
+      {
+	FIND_EOL();
+	break;
+      }
+
+      if(data[pos]=='*')
+      {
+	PUTC(' ');
+	SKIPCOMMENT();
+	break;
+      }
+
+      PUTC(data[pos-1]);
+      break;
+
+  case '#':
+    if(GOBBLE('!'))
+    {
+      FIND_EOL();
+      break;
+    }
+    SKIPSPACE();
+
+    switch(data[pos])
+    {
+    case 'l':
+      {
+	static WCHAR line_[] = { 'l', 'i', 'n', 'e' };
+
+	if(WGOBBLE2(line_, 4))
+	{
+	  /* FIXME: Why not use SKIPSPACE()? */
+	  while(data[pos]==' ' || data[pos]=='\t') pos++;
+	}else{
+	  goto unknown_preprocessor_directive;
+	}
+      /* Fall through */
+      }
+    case '0': case '1': case '2': case '3': case '4':
+    case '5': case '6': case '7': case '8': case '9':
+    {
+      PCHARP foo = MKPCHARP(data+pos, SHIFT);
+      this->current_line=STRTOL_PCHARP(foo, &foo, 10)-1;
+      string_builder_putchar(&this->buf, '#');
+      string_builder_append(&this->buf, MKPCHARP(data+pos, SHIFT),
+			    ((WCHAR *)foo.ptr) - (data+pos));
+      pos = ((WCHAR *)foo.ptr) - data;
+      SKIPSPACE();
+      
+      if(data[pos]=='"')
+      {
+	struct string_builder nf;
+	init_string_builder(&nf, 0);
+	
+	READSTRING(nf);
+
+	free_string(this->current_file);
+	this->current_file = finish_string_builder(&nf);
+
+	string_builder_putchar(&this->buf, ' ');
+	switch (this->current_file->size_shift) {
+	case 0:
+	  PUSH_STRING0((p_wchar0 *)this->current_file->str,
+		       this->current_file->len,&this->buf);
+	  break;
+	case 1:
+	  PUSH_STRING1((p_wchar1 *)this->current_file->str,
+		       this->current_file->len,&this->buf);
+	  break;
+	case 2:
+	  PUSH_STRING2((p_wchar2 *)this->current_file->str,
+		       this->current_file->len,&this->buf);
+	  break;
+	default:
+	  fatal("cpp(): Filename has bad shift %d\n",
+		this->current_file->size_shift);
+	  break;
+	}
+      }
+      
+      FIND_EOL();
+      break;
+    }
+
+      case '"':
+      {
+	struct string_builder nf;
+	init_string_builder(&nf, 0);
+	
+	READSTRING2(nf);
+	if(OUTP())
+	{
+	  PUSH_STRING((WCHAR *)nf.s->str, nf.s->len, &this->buf);
+	}
+	free_string_builder(&nf);
+	break;
+      }
+
+      case 's':
+	{
+	  WCHAR string_[] = { 's', 't', 'r', 'i', 'n', 'g' };
+
+	  if(WGOBBLE2(string_, 6))
+	  {
+	    tmp2=1;
+	    goto do_include;
+	  }
+	}
+      goto unknown_preprocessor_directive;
+
+    case 'i': /* include, if, ifdef */
+      {
+	static WCHAR include_[] = { 'i', 'n', 'c', 'l', 'u', 'd', 'e' };
+	static WCHAR if_[] = { 'i', 'f' };
+	static WCHAR ifdef_[] = { 'i', 'f', 'd', 'e', 'f' };
+	static WCHAR ifndef_[] = { 'i', 'f', 'n', 'd', 'e', 'f' };
+
+      if(WGOBBLE2(include_, 7))
+      {
+	tmp2=0;
+      do_include:
+	{
+	  struct svalue *save_sp=sp;
+	  SKIPSPACE();
+	  
+	  check_stack(3);
+	  
+	  switch(data[pos++])
+	  {
+	    case '"':
+	      {
+		struct string_builder nf;
+		init_string_builder(&nf, 0);
+		pos--;
+		READSTRING(nf);
+		push_string(finish_string_builder(&nf));
+		ref_push_string(this->current_file);
+		push_int(1);
+		break;
+	      }
+
+	    case '<':
+	      {
+		INT32 tmp=pos;
+		while(data[pos]!='>')
+		{
+		  if(data[pos]=='\n')
+		  {
+		    cpp_error(this,"Expecting '>' in include.");
+		    break;
+		  }
+		      
+		  pos++;
+		}
+#if (SHIFT == 0)
+		push_string(make_shared_binary_string(data+tmp, pos-tmp));
+#else /* SHIFT != 0 */
+#if (SHIFT == 1)
+		push_string(make_shared_binary_string1(data+tmp, pos-tmp));
+#else /* SHIFT != 1 */
+		push_string(make_shared_binary_string2(data+tmp, pos-tmp));
+#endif /* SHIFT == 1 */
+#endif /* SHIFT == 0 */
+		ref_push_string(this->current_file);
+		pos++;
+		push_int(0);
+		break;
+	      }
+
+	    default:
+	      cpp_error(this,"Expected file to include.");
+	      break;
+	    }
+
+	  if(sp==save_sp) break;
+
+	  if(OUTP())
+	  {
+	    struct pike_string *new_file;
+
+	    SAFE_APPLY_MASTER("handle_include",3);
+	  
+	    if(sp[-1].type != T_STRING)
+	    {
+	      cpp_error(this,"Couldn't include file.");
+	      pop_n_elems(sp-save_sp);
+	      break;
+	    }
+	    
+	    new_file=sp[-1].u.string;
+	    
+	    assign_svalue_no_free(sp,sp-1);
+	    sp++;
+	    
+	    SAFE_APPLY_MASTER("read_include",1);
+	    
+	    if(sp[-1].type != T_STRING)
+	    {
+	      cpp_error(this,"Couldn't read include file.");
+	      pop_n_elems(sp-save_sp);
+	      break;
+	    }
+	    
+	    {
+	      char buffer[47];
+	      struct pike_string *save_current_file;
+	      INT32 save_current_line;
+
+	      save_current_file=this->current_file;
+	      save_current_line=this->current_line;
+	      copy_shared_string(this->current_file,new_file);
+	      this->current_line=1;
+	      
+	      string_builder_binary_strcat(&this->buf, "# 1 ", 4);
+	      PUSH_STRING_SHIFT(new_file->str, new_file->len,
+				new_file->size_shift, &this->buf);
+	      string_builder_putchar(&this->buf, '\n');
+	      if(tmp2)
+	      {
+		struct pike_string *str = sp[-1].u.string;
+		PUSH_STRING_SHIFT(str->str, str->len, str->size_shift,
+				  &this->buf);
+	      }else{
+		low_cpp(this,
+			sp[-1].u.string->str,
+			sp[-1].u.string->len,
+			sp[-1].u.string->size_shift,
+			flags&~(CPP_EXPECT_ENDIF | CPP_EXPECT_ELSE));
+	      }
+	      
+	      free_string(this->current_file);
+	      this->current_file=save_current_file;
+	      this->current_line=save_current_line;
+	      
+	      sprintf(buffer,"# %d ",this->current_line);
+	      string_builder_binary_strcat(&this->buf, buffer, strlen(buffer));
+	      PUSH_STRING_SHIFT(this->current_file->str,
+				this->current_file->len,
+				this->current_file->size_shift,
+				&this->buf);
+	      string_builder_putchar(&this->buf, '\n');
+	    }
+	  }
+
+	  pop_n_elems(sp-save_sp);
+	  
+	  break;
+	}
+      }
+
+      if(WGOBBLE2(if_, 2))
+      {
+	struct string_builder save,tmp;
+	INT32 nflags=CPP_EXPECT_ELSE | CPP_EXPECT_ENDIF;
+	
+	if(!OUTP())
+	  nflags|=CPP_REALLY_NO_OUTPUT;
+	
+	save=this->buf;
+	init_string_builder(&this->buf, SHIFT);
+	pos+=lower_cpp(this, data+pos, len-pos, CPP_END_AT_NEWLINE|CPP_DO_IF);
+	tmp=this->buf;
+	this->buf=save;
+	
+	string_builder_putchar(&tmp, 0);
+	tmp.s->len--;
+	
+	calc(this, (WCHAR *)tmp.s->str, tmp.s->len,0);
+	free_string_builder(&tmp);
+	if(IS_ZERO(sp-1)) nflags|=CPP_NO_OUTPUT;
+	pop_stack();
+	pos += lower_cpp(this, data+pos, len-pos, nflags);
+	break;
+      }
+
+      if(WGOBBLE2(ifdef_, 5))
+	{
+	  INT32 namestart,nflags;
+	  struct pike_string *s;
+	  SKIPSPACE();
+
+	  if(!WC_ISIDCHAR(data[pos]))
+	    cpp_error(this,"#ifdef what?\n");
+
+	  namestart=pos;
+	  while(WC_ISIDCHAR(data[pos])) pos++;
+	  nflags=CPP_EXPECT_ELSE | CPP_EXPECT_ENDIF | CPP_NO_OUTPUT;
+
+	  if(!OUTP())
+	    nflags|=CPP_REALLY_NO_OUTPUT;
+
+	  if((s=WC_BINARY_FINDSTRING(data+namestart, pos-namestart)))
+	    if(find_define(s))
+	      nflags&=~CPP_NO_OUTPUT;
+
+	  pos += lower_cpp(this, data+pos, len-pos, nflags);
+	  break;
+	}
+
+      if(WGOBBLE2(ifndef_, 6))
+	{
+	  INT32 namestart,nflags;
+	  struct pike_string *s;
+	  SKIPSPACE();
+
+	  if(!WC_ISIDCHAR(data[pos]))
+	    cpp_error(this,"#ifndef what?");
+
+	  namestart=pos;
+	  while(WC_ISIDCHAR(data[pos])) pos++;
+	  nflags=CPP_EXPECT_ELSE | CPP_EXPECT_ENDIF;
+
+	  if(!OUTP())
+	    nflags|=CPP_REALLY_NO_OUTPUT;
+
+	  if((s = WC_BINARY_FINDSTRING(data+namestart, pos-namestart)))
+	    if(find_define(s))
+	      nflags|=CPP_NO_OUTPUT;
+
+	  pos += lower_cpp(this, data+pos, len-pos, nflags);
+	  break;
+	}
+
+      goto unknown_preprocessor_directive;
+      }
+    case 'e': /* endif, else, elif, error */
+      {
+	static WCHAR endif_[] = { 'e', 'n', 'd', 'i', 'f' };
+	static WCHAR else_[] = { 'e', 'l', 's', 'e' };
+	static WCHAR elseif_[] = { 'e', 'l', 's', 'e', 'i', 'f' };
+	static WCHAR elif_[] = { 'e', 'l', 'i', 'f' };
+	static WCHAR error_[] = { 'e', 'r', 'r', 'o', 'r' };
+
+      if(WGOBBLE2(endif_, 5))
+      {
+	if(!(flags & CPP_EXPECT_ENDIF))
+	  cpp_error(this,"Unmatched #endif");
+
+	return pos;
+      }
+
+      if(WGOBBLE2(else_, 4))
+	{
+	  if(!(flags & CPP_EXPECT_ELSE))
+	    cpp_error(this,"Unmatched #else");
+
+	  flags&=~CPP_EXPECT_ELSE;
+	  flags|=CPP_EXPECT_ENDIF;
+
+	  if(flags & CPP_NO_OUTPUT)
+	    flags&=~CPP_NO_OUTPUT;
+	  else
+	    flags|=CPP_NO_OUTPUT;
+
+	  break;
+	}
+
+      if(WGOBBLE2(elif_, 4) || WGOBBLE2(elseif_, 6))
+      {
+	if(!(flags & CPP_EXPECT_ELSE))
+	  cpp_error(this,"Unmatched #elif");
+	
+	flags|=CPP_EXPECT_ENDIF;
+	
+	if(flags & CPP_NO_OUTPUT)
+	{
+	  struct string_builder save,tmp;
+	  save=this->buf;
+	  init_string_builder(&this->buf, 0);
+	  pos += lower_cpp(this, data+pos, len-pos,
+			   CPP_END_AT_NEWLINE|CPP_DO_IF);
+	  tmp=this->buf;
+	  this->buf=save;
+	  
+	  string_builder_putchar(&tmp, 0);
+	  tmp.s->len--;
+	  
+	  calc(this, (WCHAR *)tmp.s->str, tmp.s->len, 0);
+	  free_string_builder(&tmp);
+	  if(!IS_ZERO(sp-1)) flags&=~CPP_NO_OUTPUT;
+	  pop_stack();
+	}else{
+	  FIND_EOL();
+	  flags|= CPP_NO_OUTPUT | CPP_REALLY_NO_OUTPUT;
+	}
+	break;
+      }
+
+      if(WGOBBLE2(error_, 5))
+	{
+          INT32 foo;
+          SKIPSPACE();
+          foo=pos;
+          FIND_EOL();
+	  pos++;
+	  if(OUTP())
+	  {
+#if (SHIFT == 0)
+	    push_string(make_shared_binary_string(data+foo, pos-foo));
+#else /* SHIFT != 0 */
+#if (SHIFT == 1)
+	    push_string(make_shared_binary_string1(data+foo, pos-foo));
+#else /* SHIFT != 1 */
+	    push_string(make_shared_binary_string2(data+foo, pos-foo));
+#endif /* SHIFT == 1 */
+#endif /* SHIFT == 0 */
+	    cpp_error(this,sp[-1].u.string->str);
+	  }
+	  break;
+	}
+      }
+      goto unknown_preprocessor_directive;
+
+    case 'd': /* define */
+      {
+	static WCHAR define_[] = { 'd', 'e', 'f', 'i', 'n', 'e' };
+
+      if(WGOBBLE2(define_, 6))
+	{
+	  struct string_builder str;
+	  INT32 namestart, tmp3, nameend, argno=-1;
+	  struct define *def;
+	  struct svalue *partbase,*argbase=sp;
+
+	  SKIPSPACE();
+
+	  namestart=pos;
+	  if(!WC_ISIDCHAR(data[pos]))
+	    cpp_error(this,"Define what?");
+
+	  while(WC_ISIDCHAR(data[pos])) pos++;
+	  nameend=pos;
+
+	  if(GOBBLE('('))
+	    {
+	      argno=0;
+	      SKIPWHITE();
+
+	      while(data[pos]!=')')
+		{
+		  INT32 tmp2;
+		  if(argno)
+		    {
+		      if(!GOBBLE(','))
+			cpp_error(this,"Expecting comma in macro definition.");
+		      SKIPWHITE();
+		    }
+		  tmp2=pos;
+
+		  if(!WC_ISIDCHAR(data[pos]))
+		  {
+		    cpp_error(this,"Expected argument for macro.");
+		    break;
+		  }
+
+		  while(WC_ISIDCHAR(data[pos])) pos++;
+		  check_stack(1);
+
+#if (SHIFT == 0)
+		  push_string(make_shared_binary_string(data+tmp2, pos-tmp2));
+#else /* SHIFT != 0 */
+#if (SHIFT == 1)
+		  push_string(make_shared_binary_string1(data+tmp2, pos-tmp2));
+#else /* SHIFT != 1 */
+		  push_string(make_shared_binary_string2(data+tmp2, pos-tmp2));
+#endif /* SHIFT == 1 */
+#endif /* SHIFT == 0 */
+
+		  SKIPWHITE();
+		  argno++;
+		  if(argno>=MAX_ARGS)
+		  {
+		    cpp_error(this,"Too many arguments in macro definition.");
+		    pop_stack();
+		    argno--;
+		  }
+		}
+
+	      if(!GOBBLE(')'))
+		cpp_error(this,"Missing ) in macro definition.");
+	    }
+
+	  SKIPSPACE();
+
+	  partbase=sp;
+	  init_string_builder(&str, 0);
+	  
+	  while(1)
+	  {
+	    INT32 extra=0;
+
+/*	    fprintf(stderr,"%c",data[pos]);
+	    fflush(stderr); */
+
+	    switch(data[pos++])
+	    {
+	    case '/':
+	      if(data[pos]=='/')
+	      {
+		string_builder_putchar(&str, ' ');
+		FIND_EOL();
+		continue;
+	      }
+	      
+	      if(data[pos]=='*')
+	      {
+		PUTC(' ');
+		SKIPCOMMENT();
+		continue;
+	      }
+	      
+	      string_builder_putchar(&str, '/');
+	      continue;
+	      
+	    case '0': case '1': case '2': case '3':	case '4':
+	    case '5': case '6': case '7': case '8':	case '9':
+	      string_builder_putchar(&str, data[pos-1]);
+	      while(data[pos]>='0' && data[pos]<='9')
+		string_builder_putchar(&str, data[pos++]);
+	      continue;
+	      
+	    case '#':
+	      if(GOBBLE('#'))
+	      {
+		extra=DEF_ARG_NOPRESPACE;
+		/* FIXME: Wide strings? */
+		while(str.s->len && isspace(((unsigned char *)str.s->str)[str.s->len-1]))
+		  str.s->len--;
+		if(!str.s->len && sp-partbase>1)
+		{
+#ifdef PIKE_DEBUG
+		  if(sp[-1].type != T_INT)
+		    fatal("Internal error in CPP\n");
+#endif
+		  sp[-1].u.integer|=DEF_ARG_NOPOSTSPACE;
+		}
+	      }else{
+		extra=DEF_ARG_STRINGIFY;
+	      }
+	      SKIPSPACE();
+	      pos++;
+	      /* fall through */
+	      
+	    default:
+	      if(WC_ISIDCHAR(data[pos-1]))
+	      {
+		struct pike_string *s;
+		tmp3=pos-1;
+		while(WC_ISIDCHAR(data[pos])) pos++;
+		if(argno>0)
+		{
+		  if((s=WC_BINARY_FINDSTRING(data+tmp3, pos-tmp3)))
+		  {
+		    for(e=0;e<argno;e++)
+		    {
+		      if(argbase[e].u.string == s)
+		      {
+			check_stack(2);
+			push_string(finish_string_builder(&str));
+			init_string_builder(&str, 0);
+			push_int(e | extra);
+			extra=0;
+			break;
+		      }
+		    }
+		    if(e!=argno) continue;
+		  }
+		}
+		string_builder_append(&str, MKPCHARP(data+tmp3, SHIFT),
+				      pos-tmp3);
+	      }else{
+		string_builder_putchar(&str, data[pos-1]);
+	      }
+	      extra=0;
+	      continue;
+	      
+	    case '"':
+	      FIXSTRING(str, 1);
+	      continue;
+	      
+	    case '\'':
+	      tmp3=pos-1;
+	      FIND_END_OF_CHAR();
+	      string_builder_append(&str, MKPCHARP(data+tmp3, SHIFT),
+				    pos-tmp3);
+	      continue;
+	      
+	    case '\\':
+	      if(GOBBLE('\n'))
+	      { 
+		this->current_line++;
+		PUTNL();
+	      }
+	      continue;
+	      
+	    case '\n':
+		PUTNL();
+		this->current_line++;
+	    case 0:
+		break;
+	    }
+	    push_string(finish_string_builder(&str));
+	    break;
+	  }
+
+	  if(OUTP())
+	  {
+
+#if (SHIFT == 0)
+	    def=alloc_empty_define(
+		  make_shared_binary_string(data+namestart,
+					    nameend-namestart),
+		  (sp-partbase)/2);
+#else /* SHIFT != 0 */
+#if (SHIFT == 1)
+	    def=alloc_empty_define(
+		  make_shared_binary_string1(data+namestart,
+					     nameend-namestart),
+		  (sp-partbase)/2);
+#else /* SHIFT != 1 */
+	    def=alloc_empty_define(
+		  make_shared_binary_string2(data+namestart,
+					     nameend-namestart),
+		  (sp-partbase)/2);
+#endif /* SHIFT == 1 */
+#endif /* SHIFT == 0 */
+	    copy_shared_string(def->first, partbase->u.string);
+	    def->args=argno;
+	    
+	    for(e=0;e<def->num_parts;e++)
+	    {
+#if 1
+	      if(partbase[e*2+1].type != T_INT)
+		fatal("Cpp internal error, expected integer!\n");
+	      
+	      if(partbase[e*2+2].type != T_STRING)
+		fatal("Cpp internal error, expected string!\n");
+#endif
+	      def->parts[e].argument=partbase[e*2+1].u.integer;
+	      copy_shared_string(def->parts[e].postfix,
+				 partbase[e*2+2].u.string);
+	    }
+	    
+#ifdef PIKE_DEBUG
+	    if(def->num_parts==1 &&
+	       (def->parts[0].argument & DEF_ARG_MASK) > MAX_ARGS)
+	      fatal("Internal error in define\n");
+#endif	  
+	    
+	    this->defines=hash_insert(this->defines, & def->link);
+	    
+	  }
+	  pop_n_elems(sp-argbase);
+	  break;
+	}
+      
+      goto unknown_preprocessor_directive;
+      }
+    case 'u': /* undefine */
+      {
+	static WCHAR undefine_[] = { 'u', 'n', 'd', 'e', 'f', 'i', 'n', 'e' };
+
+	/* NOTE: Reuses undefine_ for undef_ */
+      if(WGOBBLE2(undefine_, 8) || WGOBBLE2(undefine_, 5))
+	{
+	  INT32 tmp;
+	  struct pike_string *s;
+
+	  SKIPSPACE();
+
+	  tmp=pos;
+	  if(!WC_ISIDCHAR(data[pos]))
+	    cpp_error(this,"Undefine what?");
+
+	  while(WC_ISIDCHAR(data[pos])) pos++;
+
+	  /* #undef some_long_identifier
+	   *        ^                   ^
+	   *        tmp               pos
+	   */
+
+	  if(OUTP())
+	  {
+	    if((s = WC_BINARY_FINDSTRING(data+tmp, pos-tmp)))
+	      undefine(this,s);
+	  }
+
+	  break;
+	}
+
+      goto unknown_preprocessor_directive;
+      }
+    case 'c': /* charset */
+      {
+	static WCHAR charset_[] = { 'c', 'h', 'a', 'r', 's', 'e', 't' };
+
+      if (WGOBBLE2(charset_, 7)) {
+	/* FIXME: Should probably only be allowed in 8bit strings.
+	 *
+	 * FIXME: Should probably only be allowed at top-level (flags == 0).
+	 */
+
+	INT32 p;
+	struct pike_string *s;
+
+	SKIPSPACE();
+
+	p = pos;
+	while(data[pos] && !isspace(data[pos])) {
+	  pos++;
+	}
+
+	if (pos != p) {
+	  /* The rest of the string. */
+	  s = begin_shared_string(len - pos);
+	  MEMCPY(s->str, data + pos, len - pos);
+	  push_string(end_shared_string(s));
+
+	  /* The charset name */
+	  s = begin_shared_string(pos - p);
+	  MEMCPY(s->str, data + p, pos - p);
+	  push_string(end_shared_string(s));
+
+	  SAFE_APPLY_MASTER("decode_charset", 2);
+
+	  if (sp[-1].type != T_STRING) {
+	    pop_stack();
+	    cpp_error(this, "Unknown charset.");
+	  }
+
+	  low_cpp(this, sp[-1].u.string->str, sp[-1].u.string->len,
+		  sp[-1].u.string->size_shift, flags);
+	  pop_stack();
+
+	  /* FIXME: Is this the correct thing to return? */
+	  return len;
+	} else {
+	  cpp_error(this, "What charset?");
+	}
+	
+	break;
+      }
+      goto unknown_preprocessor_directive;
+      }
+    case 'p': /* pragma */
+      {
+	static WCHAR pragma_[] = { 'p', 'r', 'a', 'g', 'm', 'a' };
+
+	if(WGOBBLE2(pragma_, 6))
+	{
+	  if(OUTP())
+	    STRCAT("#pragma", 7);
+	  else
+	    FIND_EOL();
+	  break;
+	}
+      }
+    default:
+    unknown_preprocessor_directive:
+      {
+	WCHAR buffer[180];
+	int i;
+	for(i=0; i<180-1; i++)
+	{
+	  if(!WC_ISIDCHAR(data[pos])) break;
+	  buffer[i]=data[pos++];
+	}
+	buffer[i]=0;
+	
+	cpp_error(this,"Unknown preprocessor directive.");
+      }
+    }
+    }
+  }
+
+  if(flags & CPP_EXPECT_ENDIF)
+    error("End of file while searching for #endif\n");
+
+  return pos;
+}
+
+/*
+ * Clear the defines for the next pass
+ */
+
+#undef WCHAR
+#undef WC_ISSPACE
+#undef WC_ISIDCHAR
+#undef WC_BINARY_FINDSTRING
+#undef PUSH_STRING
+#undef STRCAT
+#undef WC_STRCAT
+
+#undef lower_cpp
+#undef find_end_parenthesis
+
+#undef calc	calc_2
+#undef calc1	calc1_2
+#undef calc2	calc2_2
+#undef calc3	calc3_2
+#undef calc4	calc4_2
+#undef calc5	calc5_2
+#undef calc6	calc6_2
+#undef calc7	calc7_2
+#undef calc7b	calc7b_2
+#undef calc8	calc8_2
+#undef calc9	calc9_2
+#undef calcA	calcA_2
+#undef calcB	calcB_2
+#undef calcC	calcC_2
+