/*\ ||| 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 }