/*\
||| This file a part of Pike, and is copyright by Fredrik Hubinette
||| Pike is distributed as GPL (General Public License)
||| See the files COPYING and DISCLAIMER for more information.
\*/
/**/
#include "global.h"
RCSID("$Id: iterators.cmod,v 1.13 2001/06/17 15:59:05 grubba Exp $");
#include "main.h"
#include "object.h"
#include "mapping.h"
#include "multiset.h"
#include "svalue.h"
#include "array.h"
#include "pike_macros.h"
#include "pike_error.h"
#include "pike_memory.h"
#include "dynamic_buffer.h"
#include "interpret.h"
#include "las.h"
#include "gc.h"
#include "stralloc.h"
#include "security.h"
#include "block_alloc.h"
#include "opcodes.h"
#include "pike_error.h"
#include "program.h"
#include "operators.h"
#include "builtin_functions.h"
#include "constants.h"

DECLARATIONS

PIKECLASS mapping_iterator
{
  /* All variables *must* be before all functions! */
  CVAR int bucket;
  CVAR struct mapping *m;
  CVAR struct mapping_data *md;
  CVAR struct keypair *current;

  PIKEFUN mixed value()
    {
      if(THIS->current)
	push_svalue(& THIS->current->val);
      else
      {
	push_int(0);
	Pike_sp[-1].subtype=NUMBER_UNDEFINED;
      }
    }

  PIKEFUN mixed index()
    {
      if(THIS->current)
	push_svalue(& THIS->current->ind);
      else
      {
	push_int(0);
	Pike_sp[-1].subtype=NUMBER_UNDEFINED;
      }
    }

  static int step_bucket(struct mapping_iterator_struct *i)
    {
      while(! i->current)
      {
	i->bucket++;
	if(i->bucket >= i->md->hashsize)
	  return 0;
	i->current=i->md->hash[i->bucket];
      }
      return 1;
    }

  static int mi_step(struct mapping_iterator_struct *i)
    {
      if(! i->current) return 0;
      i->current=i->current->next;
      return step_bucket(i);
    }
  
  PIKEFUN object `+(int steps)
    {
      struct object *o=low_clone(mapping_iterator_program);
      OBJ2_MAPPING_ITERATOR(o)[0] = *THIS;
      add_ref(THIS->m);
      add_ref(THIS->md);
      THIS->md->valrefs++;
      while(--steps>=0 && mi_step(OBJ2_MAPPING_ITERATOR(o)));
      RETURN o;
    }

  PIKEFUN object `+=(int steps)
    {
      while(--steps>=0 && mi_step(THIS));
      REF_RETURN Pike_fp->current_object;
    }

  PIKEFUN int first()
    {
      THIS->current=0;
      THIS->bucket=-1;
      RETURN step_bucket(THIS);
    }

  /* Hubbe: Should this really be destructive ?? */
  PIKEFUN object _random()
    {
      if(THIS->md->hashsize)
      {
	size_t k;
	struct keypair *tmp;
	THIS->bucket=my_rand() % THIS->md->hashsize;
	k=0;
	for(tmp=THIS->md->hash[THIS->bucket];tmp;tmp=tmp->next) k++;
	tmp=THIS->md->hash[THIS->bucket];
	if(k)
	{
	  k=my_rand() % k;
	  while(--k > 0) tmp=tmp->next;
	}
	THIS->current=tmp;
      }else{
	THIS->bucket=-1;
	THIS->current=0;
      }
      step_bucket(THIS);
      REF_RETURN Pike_fp->current_object;
    }

  PIKEFUN int next() { RETURN mi_step(THIS); }
  PIKEFUN int `!() { RETURN !THIS->current; }

  PIKEFUN void create(mapping map)
    {
      if(THIS->m)
	Pike_error("Mapping iterators cannot be reused.\n");

      add_ref(THIS->m=map);
      THIS->md=map->data;
      add_ref(THIS->md);
      THIS->md->valrefs++;
      THIS->bucket=-1;
      step_bucket(THIS);
    }

  INIT
    {
      THIS->m=0;
      THIS->md=0;
      THIS->current=0;
      THIS->bucket=0;
    }

  EXIT
    {
      free_mapping(THIS->m);
      THIS->md->valrefs--;
      free_mapping_data(THIS->md);
    }
};

PIKECLASS array_iterator
{
  CVAR int pos;
  CVAR struct array *a;
  
  PIKEFUN mixed value()
    {
      if(!THIS->a || THIS->pos >= THIS->a->size) 
      {
	push_int(0);
	Pike_sp[-1].subtype=NUMBER_UNDEFINED;
      }else{
	push_svalue(THIS->a->item + THIS->pos);
      }
    }

  PIKEFUN int index()
    {
      if(!THIS->a || THIS->pos >= THIS->a->size) 
      {
	push_int(0);
	Pike_sp[-1].subtype=NUMBER_UNDEFINED;
      }else{
	RETURN THIS->pos;
      }
    }

  PIKEFUN object `+(int steps)
    {
      struct object *o=low_clone(array_iterator_program);
      OBJ2_ARRAY_ITERATOR(o)[0]=*THIS;
      add_ref(THIS->a);
      OBJ2_ARRAY_ITERATOR(o)->pos+=steps;
      RETURN o;
    }

  PIKEFUN object `+=(int steps)
  {
    THIS->pos+=steps;
    REF_RETURN Pike_fp->current_object;
  }

  PIKEFUN int first()
    {
      THIS->pos=0;
      RETURN THIS->a && THIS->a->size < THIS->pos;
    }

  /* Hubbe: Should this really be destructive ?? */
  PIKEFUN object _random()
    {
      if(THIS->a->size)
	THIS->pos=my_rand() % THIS->a->size;
      else
	THIS->pos=0;
      REF_RETURN Pike_fp->current_object;
    }

  PIKEFUN int next()
    {
      THIS->pos++;
      RETURN THIS->a && THIS->a->size < THIS->pos;
    }

  PIKEFUN int `!()
    {
      RETURN !(THIS->a && THIS->a->size < THIS->pos);
    }

  PIKEFUN void create(array a)
    {
      if(THIS->a)
	Pike_error("Array iterators cannot be reused.\n");
      
      add_ref(THIS->a=a);
    }
  
  INIT 
    {
      THIS->a=0;
      THIS->pos=0;
    }

  EXIT
    {
      free_array(THIS->a);
    }
    
};

PIKECLASS multiset_iterator
{
  CVAR int pos;
  CVAR struct array *a;
  
  PIKEFUN int value()
    {
      if(!THIS->a || THIS->pos >= THIS->a->size) 
      {
	push_int(0);
	Pike_sp[-1].subtype=NUMBER_UNDEFINED;
      }else{
	push_int(1);
      }
    }

  PIKEFUN mixed index()
    {
      if(!THIS->a || THIS->pos >= THIS->a->size) 
      {
	push_int(0);
	Pike_sp[-1].subtype=NUMBER_UNDEFINED;
      }else{
	push_svalue(THIS->a->item + THIS->pos);
      }
    }

  PIKEFUN object `+(int steps)
    {
      struct object *o=low_clone(array_iterator_program);
      OBJ2_MULTISET_ITERATOR(o)[0]=*THIS;
      add_ref(THIS->a);
      OBJ2_MULTISET_ITERATOR(o)->pos+=steps;
      RETURN o;
    }

  PIKEFUN object `+=(int steps)
  {
    THIS->pos+=steps;
    REF_RETURN Pike_fp->current_object;
  }

  PIKEFUN int first()
    {
      THIS->pos=0;
      RETURN THIS->a && THIS->a->size < THIS->pos;
    }

  /* Hubbe: Should this really be destructive ?? */
  PIKEFUN object _random()
    {
      if(THIS->a->size)
	THIS->pos=my_rand() % THIS->a->size;
      else
	THIS->pos=0;
      REF_RETURN Pike_fp->current_object;
    }

  PIKEFUN int next()
    {
      THIS->pos++;
      RETURN THIS->a && THIS->a->size < THIS->pos;
    }

  PIKEFUN int `!()
    {
      RETURN !(THIS->a && THIS->a->size < THIS->pos);
    }

  PIKEFUN void create(multiset m)
    {
      if(THIS->a)
	Pike_error("Array iterators cannot be reused.\n");
      
      add_ref(THIS->a=m->ind);
    }
  
  INIT 
    {
      THIS->a=0;
      THIS->pos=0;
    }

  EXIT
    {
      free_array(THIS->a);
    }
    
};

PIKECLASS string_iterator
{
  CVAR int pos;
  CVAR struct pike_string *s;
  
  PIKEFUN int value()
    {
      if(!THIS->s || THIS->pos >= THIS->s->len) 
      {
	push_int(0);
	Pike_sp[-1].subtype=NUMBER_UNDEFINED;
      }else{
	RETURN index_shared_string(THIS->s, THIS->pos);
      }
    }

  PIKEFUN int index()
    {
      if(!THIS->s || THIS->pos >= THIS->s->len) 
      {
	push_int(0);
	Pike_sp[-1].subtype=NUMBER_UNDEFINED;
      }else{
	RETURN THIS->pos;
      }
    }

  PIKEFUN object `+(int steps)
    {
      struct object *o=low_clone(string_iterator_program);
      OBJ2_STRING_ITERATOR(o)[0]=*THIS;
      add_ref(THIS->s);
      OBJ2_STRING_ITERATOR(o)->pos+=steps;
      RETURN o;
    }

  PIKEFUN object `+=(int steps)
  {
    THIS->pos+=steps;
    REF_RETURN Pike_fp->current_object;
  }

  PIKEFUN int first()
    {
      THIS->pos=0;
      RETURN THIS->s && THIS->s->len < THIS->pos;
    }

  /* Hubbe: Should this really be destructive ?? */
  PIKEFUN object _random()
    {
      if(THIS->s->len)
	THIS->pos=my_rand() % THIS->s->len;
      else
	THIS->pos=0;
      REF_RETURN Pike_fp->current_object;
    }

  PIKEFUN int next()
    {
      THIS->pos++;
      RETURN THIS->s && THIS->s->len < THIS->pos;
    }

  PIKEFUN int `!()
    {
      RETURN !(THIS->s && THIS->s->len < THIS->pos);
    }

  PIKEFUN void create(string s)
    {
      if(THIS->s)
	Pike_error("String iterators cannot be reused.\n");
      
      add_ref(THIS->s=s);
    }
  
  INIT 
    {
      THIS->s=0;
      THIS->pos=0;
    }

  EXIT
    {
      free_string(THIS->s);
    }
};

/* Interal (for Stdio.File) class: Stdio.File->line_iterator.
 * Splits on lines, does only handle 8-bit characters.
 */
PIKECLASS file_line_iterator
{
  CVAR struct pike_string *buffer;
  CVAR struct pike_string *current;
  CVAR int offset;
  CVAR int index;
  CVAR struct svalue feed;


  static void fl_find_next(struct file_line_iterator_struct *ssi)
  {
    unsigned char *ptr, *eptr, *start;
    int start_off = ssi->offset;

    if (ssi->current)
    {
      free_string(ssi->current);
      ssi->current = NULL;
    }

    if (!ssi->buffer)
      return;

  again:
      start = ssi->buffer->str + start_off;
      ptr   = ssi->buffer->str + ssi->offset;
      eptr  = ssi->buffer->str + ssi->buffer->len;

      /* Loop until we find a '\n'. */
      while( ptr < eptr )
	if( *ptr != '\n'  )
	  ptr++;
	else
	{
	  ssi->current = make_shared_binary_string( start, ptr-start );
	  // skip the '\n'
	  ssi->offset =  ptr - ((unsigned char *)ssi->buffer->str) + 1;
	  ssi->index++;
	  return;
	}

      apply_svalue( &ssi->feed, 0 );
      if( Pike_sp[-1].type == PIKE_T_STRING &&
	  Pike_sp[-1].u.string->len )
      {
	push_string(make_shared_binary_string( ssi->buffer->str+start_off,
					       ssi->buffer->len-start_off));
	free_string( ssi->buffer );
	stack_swap();
	f_add( 2 );
	ssi->buffer = Pike_sp[-1].u.string;
	Pike_sp--;

	if( !ssi->buffer->size_shift )
	{
	  ssi->offset -= start_off;
	  start_off = 0;
	  goto again;
	}
      }
      else
	pop_stack();
      free_string( ssi->buffer );
      ssi->buffer = NULL;
      return;
   }
  
  PIKEFUN string value()
    {
      if (THIS->current) {
	ref_push_string(THIS->current);
      } else {
	push_int(0);
	Pike_sp[-1].subtype = NUMBER_UNDEFINED;
      }
    }

  PIKEFUN int index()
    {
      if (!THIS->current) {
	push_int(0);
	Pike_sp[-1].subtype = NUMBER_UNDEFINED;
      }else{
	RETURN THIS->index;
      }
    }

  PIKEFUN object `+(int steps)
    {
      struct object *o=low_clone(file_line_iterator_program);
      int i;
      struct file_line_iterator_struct *ssi;
      (ssi = OBJ2_FILE_LINE_ITERATOR(o))[0]=*THIS;
      if (THIS->buffer) {
	add_ref(THIS->buffer);
      }
      if (THIS->current) {
	add_ref(THIS->current);
      }
      add_ref_svalue(&THIS->feed);
      while( steps-- )
	fl_find_next(ssi);
      RETURN o;
    }

  PIKEFUN object `+=(int steps)
    {
      while( steps-- )
	fl_find_next(THIS);
      REF_RETURN Pike_fp->current_object;
    }

  PIKEFUN int first()
    {
      Pike_error("Not supported.\n");
    }

  PIKEFUN object _random()
    {
      Pike_error("Not supported.\n");
    }

  PIKEFUN int _sizeof()
    {
      Pike_error("Not supported.\n");
    }

  PIKEFUN int next()
    {
      fl_find_next(THIS);
      RETURN !!THIS->current;
    }

  PIKEFUN int `!()
    {
      RETURN !THIS->current;
    }

  PIKEFUN void create(function(:string)|void feed)
    {
      if (THIS->buffer) {
	Pike_error("iterators cannot be reused.\n");
      }
      assign_svalue( &THIS->feed, feed );
      apply_svalue( &THIS->feed, 0 );
      if( Pike_sp[-1].type != PIKE_T_STRING )
	Pike_error("Feed function returned illegal value\n");
      THIS->buffer = Pike_sp[-1].u.string;
      Pike_sp--;
      THIS->offset = 0;
      THIS->current = NULL;
      THIS->index = -1;
      fl_find_next(THIS);
    }
  
  INIT 
    {
      THIS->buffer = NULL;
      THIS->current = NULL;
      THIS->offset = 0;
      THIS->index = 0;
      THIS->feed.type = T_INT;
      THIS->feed.u.integer = 0;
    }

  EXIT
    {
      if (THIS->buffer) {
	free_string(THIS->buffer);
	THIS->buffer = NULL;
      }
      if (THIS->current) {
	free_string(THIS->current);
	THIS->current = NULL;
      }
      free_svalue(&THIS->feed);
      THIS->feed.type = T_INT;
      THIS->feed.u.integer = 0;
    }
  
  
}

/*! @module String
 */

/*! @class SplitIterator
 */
PIKECLASS string_split_iterator
{
  CVAR struct pike_string *buffer;
  CVAR struct pike_string *current;
  CVAR int offset;
  CVAR int index;
  CVAR p_wchar2 *split_set;
  CVAR int split_set_size;
  CVAR int flags;
  CVAR struct svalue feed;

#define SIMPLE_SKIP(THIS, SHIFT, OFF) 	 	\
  do {						\
    PIKE_CONCAT(p_wchar, SHIFT) *s = 		\
      PIKE_CONCAT(STR,SHIFT)(THIS->buffer);	\
    while((OFF < THIS->buffer->len) &&		\
          s[OFF] == THIS->split_set[0]) {	\
      OFF++;					\
    }						\
  } while(0)

#define SIMPLE_SKIP_CASE(THIS, SHIFT, OFF)	\
  case SHIFT:					\
    SIMPLE_SKIP(THIS, SHIFT, OFF);		\
    break

#define COMPLEX_SKIP(THIS, SHIFT, OFF)	 		\
  do {							\
    PIKE_CONCAT(p_wchar, SHIFT) *s = 			\
      PIKE_CONCAT(STR,SHIFT)(THIS->buffer);		\
    while(OFF < THIS->buffer->len) {			\
      int i;						\
      p_wchar2 ch = s[OFF];				\
      for (i=0; i < THIS->split_set_size; i++) {	\
	if (ch == THIS->split_set[i]) {			\
	  goto PIKE_CONCAT(continue_skip,SHIFT);	\
	}						\
      }							\
      break;						\
    PIKE_CONCAT(continue_skip,SHIFT):			\
      OFF++;						\
    }							\
  } while(0)
  
#define COMPLEX_SKIP_CASE(THIS, SHIFT, OFF)	\
  case SHIFT:					\
    COMPLEX_SKIP(THIS, SHIFT, OFF);		\
    break

#define SIMPLE_SCAN(THIS, SHIFT, OFF)		\
  do {						\
    PIKE_CONCAT(p_wchar, SHIFT) *s = 		\
      PIKE_CONCAT(STR,SHIFT)(THIS->buffer);	\
    while((OFF < THIS->buffer->len) &&		\
          s[OFF] != THIS->split_set[0]) {	\
      OFF++;					\
    }						\
  } while(0)

#define SIMPLE_SCAN_CASE(THIS, SHIFT, OFF)	\
  case SHIFT:					\
    SIMPLE_SCAN(THIS, SHIFT, OFF);		\
    break

#define COMPLEX_SCAN(THIS, SHIFT, OFF)				\
  do {								\
    PIKE_CONCAT(p_wchar, SHIFT) *s = 				\
      PIKE_CONCAT(STR,SHIFT)(THIS->buffer);			\
    while(OFF < THIS->buffer->len) {				\
      int i;							\
      p_wchar2 ch = s[OFF];					\
      for (i=0; i < THIS->split_set_size; i++) {		\
	if (ch == THIS->split_set[i]) {				\
	  goto PIKE_CONCAT(break_scan,SHIFT);			\
	}							\
      }								\
      OFF++;							\
    }								\
  PIKE_CONCAT(break_scan, SHIFT): ;/* gcc complains without ;*/ \
  } while(0)

#define COMPLEX_SCAN_CASE(THIS, SHIFT, OFF)	\
  case SHIFT:					\
    COMPLEX_SCAN(THIS, SHIFT, OFF);		\
    break

#define SIMPLE_SCAN_PUSH(THIS, SHIFT, OFF)			\
  do {								\
    PIKE_CONCAT(p_wchar, SHIFT) *s = 				\
      PIKE_CONCAT(STR,SHIFT)(THIS->buffer);			\
    while((OFF < THIS->buffer->len) &&				\
          s[OFF] != THIS->split_set[0]) {			\
      OFF++;							\
    }								\
    push_string(PIKE_CONCAT(make_shared_binary_string, SHIFT)	\
      (s+offset, OFF-offset));					\
  } while(0)

#define SIMPLE_SCAN_CASE_PUSH(THIS, SHIFT, OFF)	\
  case SHIFT:					\
    SIMPLE_SCAN_PUSH(THIS, SHIFT, OFF);		\
    break

#define COMPLEX_SCAN_PUSH(THIS, SHIFT, OFF)			\
  do {								\
    PIKE_CONCAT(p_wchar, SHIFT) *s = 				\
      PIKE_CONCAT(STR,SHIFT)(THIS->buffer);			\
    while(OFF < THIS->buffer->len) {				\
      int i;							\
      p_wchar2 ch = s[OFF];					\
      for (i=0; i < THIS->split_set_size; i++) {		\
	if (ch == THIS->split_set[i]) {				\
	  goto PIKE_CONCAT(break_scan,SHIFT);			\
	}							\
      }								\
      OFF++;							\
    }								\
  PIKE_CONCAT(break_scan, SHIFT):				\
    push_string(PIKE_CONCAT(make_shared_binary_string, SHIFT)	\
      (s+offset, OFF-offset));					\
  } while(0)

#define COMPLEX_SCAN_CASE_PUSH(THIS, SHIFT, OFF)	\
  case SHIFT:						\
    COMPLEX_SCAN_PUSH(THIS, SHIFT, OFF);		\
    break

#define NEW_SKIP_CASE(SHIFT, FLAGS)				\
  case SHIFT:							\
    if (FLAGS) {						\
      /* Skip empty */						\
      if (ssi->split_set_size == 1) {				\
	SIMPLE_SKIP(ssi, SHIFT, offset);			\
      } else {							\
        COMPLEX_SKIP(ssi, SHIFT, offset);			\
      }								\
    }								\
    if (offset >= ssi->buffer->len) {				\
      free_string(ssi->buffer);					\
      ssi->buffer = NULL;					\
      ssi->offset = 0;						\
      offset = 0;						\
      if (ssi->feed.type == T_INT) {				\
	if (!(FLAGS)) {						\
	  MAKE_CONSTANT_SHARED_STRING(ssi->current, "");	\
	  ssi->index++;						\
	}							\
	return;							\
      } else {							\
	/* Attempt to fill the buffer with some more. */	\
	apply_svalue(&ssi->feed, 0);				\
	if ((Pike_sp[-1].type == T_STRING) &&			\
	    (Pike_sp[-1].u.string->len)) {			\
	  ssi->buffer = Pike_sp[-1].u.string;			\
	  Pike_sp--;						\
	  goto reskip_empty;					\
	}							\
	free_svalue(&ssi->feed);				\
	ssi->feed.type = T_INT;					\
	ssi->feed.u.integer = 0;				\
	pop_stack();						\
	if (!(FLAGS)) {						\
	  MAKE_CONSTANT_SHARED_STRING(ssi->current, "");	\
	  ssi->index++;						\
	}							\
	return;							\
      }								\
    }								\
    ssi->index++;						\
    end = offset;						\
    goto PIKE_CONCAT(scan_more_,SHIFT)

#define NEW_SCAN_MORE_CASE(SHIFT)				\
  case SHIFT:							\
  PIKE_CONCAT(scan_more_,SHIFT):				\
    if (ssi->split_set_size == 1) {				\
      SIMPLE_SCAN_PUSH(ssi, SHIFT, end);			\
    } else {							\
      COMPLEX_SCAN_PUSH(ssi, SHIFT, end);			\
    }								\
    if ((end == ssi->buffer->len) &&				\
	(ssi->feed.type != T_INT)) {				\
      apply_svalue(&ssi->feed, 0);				\
      if ((Pike_sp[-1].type == T_STRING) &&			\
	  (Pike_sp[-1].u.string->len)) {			\
	f_add(2);						\
	if (Pike_sp[-1].type != T_STRING) {			\
	  Pike_error("Bad result from concatenation!\n");	\
	}							\
	free_string(ssi->buffer);				\
	ssi->buffer = Pike_sp[-1].u.string;			\
	Pike_sp--;						\
	end -= offset;						\
	offset = 0;						\
	goto scan_more;						\
      }								\
      pop_stack();	/* Pop the end of stream marker. */	\
      								\
      /* Make sure we don't call feed() any more. */		\
      free_svalue(&ssi->feed);					\
      ssi->feed.type = T_INT;					\
      ssi->feed.u.integer = 0;					\
    }								\
    ssi->offset = end+1;					\
    ssi->current = Pike_sp[-1].u.string;			\
    Pike_sp--;							\
    if (ssi->offset > ssi->buffer->len) {			\
      free_string(ssi->buffer);					\
      ssi->buffer = 0;						\
    }								\
    break
  
  static void find_next(struct string_split_iterator_struct *ssi)
    {
      int offset = ssi->offset;
      int end;
      if (ssi->current) {
	free_string(ssi->current);
      }
      ssi->current = NULL;
      if (!ssi->buffer) {
	return;
      }
    reskip_empty:
      switch(ssi->buffer->size_shift) {
	NEW_SKIP_CASE(0, ssi->flags);
	NEW_SKIP_CASE(1, ssi->flags);
	NEW_SKIP_CASE(2, ssi->flags);
      default:
	fatal("Unsupported size shift!\n");
      }
    scan_more:
      switch(ssi->buffer->size_shift)
      {
	NEW_SCAN_MORE_CASE(0);
	NEW_SCAN_MORE_CASE(1);
	NEW_SCAN_MORE_CASE(2);
      default:
	fatal("Unsupported size shift!\n");
      }
    }

  PIKEFUN string value()
    {
      if (THIS->current) {
	ref_push_string(THIS->current);
      } else {
	push_int(0);
	Pike_sp[-1].subtype = NUMBER_UNDEFINED;
      }
    }

  PIKEFUN int index()
    {
      if (!THIS->current) {
	push_int(0);
	Pike_sp[-1].subtype = NUMBER_UNDEFINED;
      }else{
	RETURN THIS->index;
      }
    }

  PIKEFUN object `+(int steps)
    {
      struct object *o=low_clone(string_split_iterator_program);
      int i;
      struct string_split_iterator_struct *ssi;
      (ssi = OBJ2_STRING_SPLIT_ITERATOR(o))[0]=*THIS;
      if (THIS->buffer) {
	add_ref(THIS->buffer);
      }
      if (THIS->current) {
	add_ref(THIS->current);
      }
      add_ref_svalue(&THIS->feed);
      for (i=0; i < steps; i++) {
	find_next(ssi);
      }
      RETURN o;
    }

  PIKEFUN object `+=(int steps)
  {
    int i;
    for(i = 0; i < steps; i++) {
      find_next(THIS);
    }
    REF_RETURN Pike_fp->current_object;
  }

  PIKEFUN int first()
    {
      Pike_error("Not supported.\n");
    }

  PIKEFUN object _random()
    {
      Pike_error("Not supported.\n");
    }
  PIKEFUN int next()
    {
      find_next(THIS);
      RETURN !!THIS->current;
    }

  PIKEFUN int `!()
    {
      RETURN !THIS->current;
    }

  PIKEFUN int _sizeof()
  {
    INT_TYPE res = 0;
    if (THIS->buffer) {
      int i, off;
      ref_push_string(THIS->buffer);
      if (THIS->offset) {
	push_int(THIS->offset);
	push_int(THIS->buffer->len);
	o_range();
      }
      for (i = 1; THIS->feed.type != T_INT; i++) {
	apply_svalue(&THIS->feed, 0);
	if ((Pike_sp[-1].type != T_STRING) ||
	    (!Pike_sp[-1].u.string->len)) {
	  /* End of stream marker. */
	  pop_stack();
	  free_svalue(&THIS->feed);
	  THIS->feed.type = T_INT;
	  THIS->feed.u.integer = 0;
	  break;
	}
      }
      f_add(i);	/* Join the segments. */
      free_string(THIS->buffer);
      THIS->buffer = Pike_sp[-1].u.string;
      THIS->offset = 0;
      Pike_sp--;

      /* Perform the scan. */
      for (off=0; off < THIS->buffer->len; off++) {
	if (THIS->flags) {
	  if (THIS->split_set_size == 1) {
	    switch(THIS->buffer->size_shift) {
	      SIMPLE_SKIP_CASE(THIS, 0, off);
	      SIMPLE_SKIP_CASE(THIS, 1, off);
	      SIMPLE_SKIP_CASE(THIS, 2, off);
	    default:
	      fatal("Unsupported size shift!\n");
	    }
	  } else {
	    switch(THIS->buffer->size_shift) {
	      COMPLEX_SKIP_CASE(THIS, 0, off);
	      COMPLEX_SKIP_CASE(THIS, 1, off);
	      COMPLEX_SKIP_CASE(THIS, 2, off);
	    default:
	      fatal("Unsupported size shift!\n");
	    }	  
	  }
	  if (off >= THIS->buffer->len) {
	    break;
	  }
	}
	res++;
	if (THIS->split_set_size == 1) {
	  switch(THIS->buffer->size_shift) {
	    SIMPLE_SCAN_CASE(THIS, 0, off);
	    SIMPLE_SCAN_CASE(THIS, 1, off);
	    SIMPLE_SCAN_CASE(THIS, 2, off);
	  default:
	    fatal("Unsupported size shift!\n");
	  }
	} else {
	  switch(THIS->buffer->size_shift) {
	    COMPLEX_SCAN_CASE(THIS, 0, off);
	    COMPLEX_SCAN_CASE(THIS, 1, off);
	    COMPLEX_SCAN_CASE(THIS, 2, off);
	  default:
	    fatal("Unsupported size shift!\n");
	  }
	}
      }
      if ((!THIS->flags) && (off == THIS->buffer->len)) {
	/* Ends with an empty segment. */
	res++;
      }
    }
    if (THIS->current) {
      res++;
    }
    RETURN res;
  }

  PIKEFUN void create(string buffer, int|array(int)|multiset(int) split_set,
		      int|void flags, function(:string)|void feed)
    {
      if (THIS->buffer) {
	Pike_error("String.split() iterators cannot be reused.\n");
      }
      if (split_set->type == T_INT) {
	THIS->split_set = (p_wchar2 *)xalloc(sizeof(p_wchar2));
	THIS->split_set[0] = split_set->u.integer;
	THIS->split_set_size = 1;
      } else {
	struct array *a;
	int i;
	if (split_set->type == T_ARRAY) {
	  a = split_set->u.array;
	} else if (split_set->type == T_MULTISET) {
	  a = split_set->u.multiset->ind;
	} else {
	  SIMPLE_BAD_ARG_ERROR("String.split", 2,
			       "int|array(int)|multiset(int)");
	}
	if (!a->size) {
	  SIMPLE_BAD_ARG_ERROR("String.split", 2,
			       "int|array(int)|multiset(int)");
	}
	for (i=0; i < a->size; i++) {
	  if (a->item[i].type != T_INT) {
	    SIMPLE_BAD_ARG_ERROR("String.split", 2,
				 "int|array(int)|multiset(int)");
	  }
	}
	THIS->split_set = (p_wchar2 *)xalloc(a->size * sizeof(p_wchar2));
	for (i=0; i < a->size; i++) {
	  THIS->split_set[i] = a->item[i].u.integer;
	}
	THIS->split_set_size = a->size;
      }
      add_ref(THIS->buffer = buffer);
      if (args > 2) {
	if (flags->type == T_INT) {
	  THIS->flags = flags->u.integer;
	} else {
	  THIS->flags = 0;
	}
	if (args > 3) {
	  assign_svalue(&THIS->feed, feed);
	} else {
	  /* NB: THIS->feed has already been set to 0 by the init code. */
	}
      } else {
	THIS->flags = 0;
      }
      THIS->offset = 0;
      THIS->current = NULL;
      THIS->index = -1;
      find_next(THIS);
    }
  
  INIT 
    {
      THIS->buffer = NULL;
      THIS->current = NULL;
      THIS->offset = 0;
      THIS->index = 0;
      THIS->split_set = NULL;
      THIS->split_set_size = 0;
      THIS->flags = 0;
      THIS->feed.type = T_INT;
      THIS->feed.u.integer = 0;
    }

  EXIT
    {
      if (THIS->buffer) {
	free_string(THIS->buffer);
	THIS->buffer = NULL;
      }
      if (THIS->current) {
	free_string(THIS->current);
	THIS->current = NULL;
      }
      free(THIS->split_set);
      THIS->split_set = NULL;
      free_svalue(&THIS->feed);
      THIS->feed.type = T_INT;
      THIS->feed.u.integer = 0;
    }

  OPTIMIZE
    {
      if (CDR(n) && (CDR(n)->token == F_ARG_LIST) &&
	  CADR(n) && (CADR(n)->token == F_APPLY) &&
	  CAADR(n) && (CAADR(n)->token == F_CONSTANT) &&
	  (CAADR(n)->u.sval.type == T_FUNCTION) &&
	  (CAADR(n)->u.sval.subtype == FUNCTION_BUILTIN) &&
	  (CAADR(n)->u.sval.u.efun->function == f_replace)) {
	/* String.SplitIterator(replace(...),...) */
	node *repl_args = CDADR(n);
	node **str = my_get_arg(&repl_args, 0);
	node **from = my_get_arg(&repl_args, 1);
	node **to = my_get_arg(&repl_args, 2);

	if (str && from && to) {
	  /* String.SplitIterator(replace(str, from, to), ...) */

	  int num;

	  if (((*to)->token == F_APPLY) &&
	      CAR(*to) && (CAR(*to)->token == F_CONSTANT) &&
	      (CAR(*to)->u.sval.type == T_FUNCTION) &&
	      (CAR(*to)->u.sval.subtype == FUNCTION_BUILTIN) &&
	      (CAR(*to)->u.sval.u.efun->function == f_allocate) &&
	      CDR(*to) && (CDR(*to)->token == F_ARG_LIST) &&
	      CADR(*to) && (CADR(*to)->token == F_CONSTANT) &&
	      (CADR(*to)->u.sval.type == T_INT) &&
	      (num = CADR(*to)->u.sval.u.integer) &&
	      CDDR(*to) && (CDDR(*to)->token == F_CONSTANT) &&
	      (CDDR(*to)->u.sval.type == T_STRING) &&
	      (CDDR(*to)->u.sval.u.string->len == 1)) {
	    /* String.SplitIterator(replace(str, from, allocate(num, "x")),
	     *                      ...) */
	    int split_val = index_shared_string(CDDR(*to)->u.sval.u.string, 0);

	    if (CDDR(n) &&
		(((CDDR(n)->token == F_CONSTANT) &&
		  (CDDR(n)->u.sval.type == T_INT) &&
		  (CDDR(n)->u.sval.u.integer == split_val)) ||
		 ((CDDR(n)->token == F_ARG_LIST) &&
		  CADDR(n) && (CADDR(n)->token == F_CONSTANT) &&
		  (CADDR(n)->u.sval.type == T_INT) &&
		  (CADDR(n)->u.sval.u.integer == split_val)))) {
	      /* String.SplitIterator(replace(str, from, allocate(n, "x")),
	       *                      'x', ...)
	       */
	      struct array *split = NULL;
	      node *res = NULL;

	      switch((*from)->token) {
	      case F_CONSTANT:
		if (((*from)->u.sval.type == T_ARRAY) &&
		    ((*from)->u.sval.u.array->size == num)) {
		  int i;
		  for (i=0; i < num; i++) {
		    if (((*from)->u.sval.u.array->item[i].type != T_STRING) ||
			((*from)->u.sval.u.array->item[i].u.string->len != 1)) {
		      return NULL;
		    }
		  }
		  split = allocate_array(num+1);
		  split->item[0].type = T_INT;
		  split->item[0].u.integer = split_val;
		  for (i=0; i < num; i++) {
		    split->item[i+1].type = T_INT;
		    split->item[i+1].u.integer =
		      index_shared_string((*from)->u.sval.u.array->
					  item[i].u.string, 0);
		  }		  
		}
		break;
	      case F_APPLY:
		if (CAR(*from) && (CAR(*from)->token == F_CONSTANT) &&
		    (CAR(*from)->u.sval.type == T_FUNCTION) &&
		    (CAR(*from)->u.sval.subtype == FUNCTION_BUILTIN)) {
		  if (CAR(*from)->u.sval.u.efun->function == f_allocate) {
		    /* FIXME: Not likely */
		  } else if (CAR(*from)->u.sval.u.efun->function ==
			     debug_f_aggregate) {
		    node *tmp = CDR(*from);
		    int i;
		    for (i = 0; tmp && (tmp->token == F_ARG_LIST);
			 tmp = CDR(tmp)) {
		      if (!CAR(tmp)) continue;
		      if ((CAR(tmp)->token != F_CONSTANT) || 
			  (CAR(tmp)->u.sval.type != T_STRING) ||
			  (CAR(tmp)->u.sval.u.string->len != 1)) {
			return NULL;
		      }
		      i++;
		    }
		    if (i != num) {
		      return NULL;
		    }
		    split = allocate_array(num+1);
		    split->item[0].type = T_INT;
		    split->item[0].u.integer = split_val;
		    tmp = CDR(*from);
		    for (i = 1; tmp && (tmp->token == F_ARG_LIST);
			 tmp = CDR(tmp)) {
		      if (!CAR(tmp)) continue;
		      split->item[i].type = T_INT;
		      split->item[i].u.integer =
			index_shared_string(CAR(tmp)->u.sval.u.string, 0);
		      i++;
		    }		    
		  }
		} else {
		  return NULL;
		}
		break;
	      default:
		return NULL;
	      }
	      if (!split) {
		return NULL;
	      }
	      push_array(split);	/* Simplify error-handling... */

	      /* Create the result...
	       *
	       * String.SplitIterator(str, split, ...)
	       */
	      if (CDDR(n)->token == F_ARG_LIST) {
		ADD_NODE_REF2(CAR(n),
		ADD_NODE_REF2(*str,
		ADD_NODE_REF2(CDDDR(n),
		  res =
		    mkapplynode(CAR(n),
			        mknode(F_ARG_LIST, *str,
				       mknode(F_ARG_LIST,
					      mkconstantsvaluenode(Pike_sp-1),
					      CDDDR(n))));
		)));
	      } else {
		ADD_NODE_REF2(CAR(n),
		ADD_NODE_REF2(*str,
		  res =
		    mkapplynode(CAR(n),
			        mknode(F_ARG_LIST, *str,
				       mkconstantsvaluenode(Pike_sp-1)));
		));
	      }
	      pop_stack();
	      return res;
	    }
	  }
	}
      }
      return NULL;
    }
};

/*! @endclass
 */

/*! @endmodule
 */

PIKEFUN object Iterator(object|array|mapping|multiset|string data)
{
  switch(data->type)
  {
    case PIKE_T_STRING:
      push_object(clone_object(string_iterator_program, 1));
      return;

    case PIKE_T_MAPPING:
      push_object(clone_object(mapping_iterator_program,1));
      return;

    case PIKE_T_MULTISET:
      push_object(clone_object(multiset_iterator_program, 1));
      return;

    case PIKE_T_ARRAY:
      push_object(clone_object(array_iterator_program, 1));
      return;


    case PIKE_T_OBJECT:
      if(!data->u.object->prog)
	Pike_error("Argument 1 to Iterator() is a destructed object.\n");

#ifdef LFUN__GET_ITERATOR
      if(FIND_LFUN(data->u.object->prog, LFUN__GET_ITERATOR) != -1)
      {
	apply_lfun(data->u.object, LFUN__GET_ITERATOR, 1);
	stack_unlink(1);
	return;
      }
#endif

      /* Assume it already is an iterator... */
      return;

    default:
      SIMPLE_BAD_ARG_ERROR("Iterator", 1, "multiset|array|string|mapping|object");
  }
}

/* sp[-4] = index; sp[-2] = value */
int foreach_iterate(struct object *o)
{
  if(!o->prog)
    Pike_error("foreach on destructed iterator.\n");
  if(o->prog->flags & PROGRAM_HAS_C_METHODS)
  {
    if(o->prog == mapping_iterator_program)
    {
      struct mapping_iterator_struct *i=OBJ2_MAPPING_ITERATOR(o);

      if(i->current)
      {
	if(Pike_sp[-4].type != T_INT)
	  assign_lvalue(Pike_sp-4, & i->current->ind);
	
	if(Pike_sp[-2].type != T_INT)
	  assign_lvalue(Pike_sp-2, & i->current->val);
	mi_step(i);
	return 1;
      }else{
	return 0;
      }
    } else if(o->prog == string_split_iterator_program)
    {
      struct string_split_iterator_struct *i=OBJ2_STRING_SPLIT_ITERATOR(o);
      if(i->current)
      {
	if(Pike_sp[-4].type != T_INT)
	{
	  /* Black Magic... */
	  push_int(i->index);
	  Pike_sp--;
	  assign_lvalue(Pike_sp-4, Pike_sp);
	}

	if(Pike_sp[-2].type != T_INT)
	{
	  /* Black Magic... */
	  push_string(i->current);
	  Pike_sp--;
	  assign_lvalue(Pike_sp-2, Pike_sp);
	}

	find_next(i);
	return 1;
      }else{
	return 0;
      }
    } else if(o->prog == file_line_iterator_program)
    {
      struct file_line_iterator_struct *i=OBJ2_FILE_LINE_ITERATOR(o);
      if(i->current)
      {
	if(Pike_sp[-4].type != T_INT)
	{
	  /* Black Magic... */
	  push_int(i->index);
	  Pike_sp--;
	  assign_lvalue(Pike_sp-4, Pike_sp);
	}

	if(Pike_sp[-2].type != T_INT)
	{
	  /* Black Magic... */
	  push_string(i->current);
	  Pike_sp--;
	  assign_lvalue(Pike_sp-2, Pike_sp);
	}

	fl_find_next(i);
	return 1;
      }else{
	return 0;
      }
    } else if(o->prog == array_iterator_program)
    {
      struct array_iterator_struct *i=OBJ2_ARRAY_ITERATOR(o);
      if(i->pos < i->a->size)
      {
	if(Pike_sp[-4].type != T_INT)
	{
	  push_int(i->pos);
	  assign_lvalue(Pike_sp-5, Pike_sp-1);
	  pop_stack();
	}

	if(Pike_sp[-2].type != T_INT)
	  assign_lvalue(Pike_sp-2, i->a->item + i->pos);

	i->pos++;
	return 1;
      }else{
	return 0;
      }
    } else if(o->prog == multiset_iterator_program)
    {
      struct multiset_iterator_struct *i=OBJ2_MULTISET_ITERATOR(o);
      if(i->pos < i->a->size)
      {
	if(Pike_sp[-4].type != T_INT)
	  assign_lvalue(Pike_sp-4, i->a->item + i->pos);

	if(Pike_sp[-2].type != T_INT)
	{
	  push_int(1);
	  assign_lvalue(Pike_sp-3, Pike_sp-1);
	  pop_stack();
	}

	i->pos++;
	return 1;
      }else{
	return 0;
      }
    } else if(o->prog == string_iterator_program)
    {
      struct string_iterator_struct *i=OBJ2_STRING_ITERATOR(o);
      if(i->pos < i->s->len)
      {
	if(Pike_sp[-4].type != T_INT)
	{
	  push_int(i->pos);
	  assign_lvalue(Pike_sp-5, Pike_sp-1);
	  pop_stack();
	}

	if(Pike_sp[-2].type != T_INT)
	{
	  push_int(index_shared_string(i->s, i->pos));
	  assign_lvalue(Pike_sp-3, Pike_sp-1);
	  pop_stack();
	}

	i->pos++;
	return 1;
      }else{
	return 0;
      }
    }
  }

  /* Generic iteration */
  apply_lfun(o,LFUN_NOT,0);
  if(IS_ZERO(Pike_sp-1))
  {
    if(Pike_sp[-4].type != T_INT)
    {
      apply(o,"index",0);
      assign_lvalue(Pike_sp-5,Pike_sp-1);
      pop_stack();
    }

    if(Pike_sp[-2].type != T_INT)
    {
      apply(o,"value",0);
      assign_lvalue(Pike_sp-3,Pike_sp-1);
      pop_stack();
    }

    push_int(1);
    apply_lfun(o,LFUN_ADD_EQ,1);
    return 1;
  }else{
    return 0;
  }
}


void init_iterators(void)
{
  INIT
}

void exit_iterators(void)
{
  EXIT
}