#include "global.h" #ifdef HAVE_ALLOCA_H #include <alloca.h> #endif #include <sys/types.h> #include <sys/stat.h> #include "array.h" #include "interpret.h" #include "object.h" #include "regexp.h" #include "exec.h" #include "main.h" #include "stralloc.h" #include "simulate.h" #include "operators.h" #define VECTOR_SIZE(nelem) (sizeof(struct vector) +sizeof(struct svalue) * (nelem-1)) #define ALLOC_VECTOR(nelem) (struct vector *)xalloc(VECTOR_SIZE(nelem)) /* * This file contains functions used to manipulate arrays. * Some of them are connected to efuns, and some are only used internally * by the game driver. */ extern int d_flag; int num_arrays; int total_array_size; /* * Make an empty vector for everyone to use, never to be deallocated. * It is cheaper to reuse it, than to use malloc() and allocate. */ struct vector null_vector = { 1, /* Ref count, which will ensure that it will never be deallocated */ 0, /* size */ 0, /* malloced size */ 1 /* is clean */ }; struct vector *allocate_array_no_init(int n,int overhead) { int i; struct vector *p; if (!batch_mode && (n < 0 || n > MAX_ARRAY_SIZE)) error("Illegal array size.\n"); if (n == 0) { p = &null_vector; p->ref++; }else{ num_arrays++; total_array_size += VECTOR_SIZE(n); /* overhead stuff */ i=n+overhead; if(i>MAX_ARRAY_SIZE && !batch_mode) i=MAX_ARRAY_SIZE; p = ALLOC_VECTOR(i); p->malloced_size=i; p->ref = 1; p->size = n; p->flags=ref_cycle; p->types=-1; #ifdef DEBUG p->next=null_vector.next; null_vector.next=p; #endif } return p; } /* * Allocate an array of size 'n'. * Observe that the overhead is not guarateed. */ struct vector *allocate_array_with_overhead(int n,int overhead) { struct vector *p; int i; p=allocate_array_no_init(n,overhead); for (i=0; i<n; i++) SET_TO_ZERO(p->item[i]); return p; } struct vector *allocate_array(int n) { return allocate_array_with_overhead(n,0); } #ifdef DEBUG void free_vector(struct vector *p) { #ifdef MALLOC_DEBUG check_sfltable(); #endif p->ref--; if (p->ref > 0) return; if(p->size>p->malloced_size) { fatal("Impossible array."); } if(p->ref<0) fatal("Freeing array too many times.\n"); #else void real_free_vector(struct vector *p) { #endif #ifdef DEBUG if (p == &null_vector) fatal("Tried to free the zero-size shared vector.\n"); { struct vector *v; for(v=&null_vector;v;v=v->next) { if(v->next==p) { v->next=p->next; break; } } if(!v) fatal("Array not found in linked list.\n"); if((v=null_vector.next)) { do{ if(!v->ref) fatal("Extremely fatal error in arrays.\n"); }while(v->size && v->item[0].type==T_POINTER && (v=v->item[0].u.vec)); } } #endif free_svalues(p->item,p->size); num_arrays--; total_array_size -= sizeof (struct vector) + sizeof (struct svalue) * (p->size-1); free((char *)p); } /* OBS, destructive */ struct vector *resize_array(struct vector *p,int n) { struct vector *res; int e; if(p->size==n) return p; if(n>p->size) { /* we should grow the array */ if(n<=p->malloced_size) { /* it fits */ p->types|=BT_NUMBER; for(e=p->size+1;e<n;e++) SET_TO_ZERO(p->item[e]); e=n-p->size; total_array_size +=sizeof(struct svalue) *e ; p->size=n; return p; } res=allocate_array_no_init(n,(n>>4)+1); for(e=0;e<p->size;e++) res->item[e]=p->item[e]; for(;e<n;e++) SET_TO_ZERO(res->item[e]); res->types=p->types | BT_NUMBER; p->size=0; /* ugly trick */ free_vector(p); return res; }else{ /* we should shrink the array */ if(n<=(p->malloced_size>>11)) { /* overhead to big, reallocate the array */ res = slice_array(p, 0, n-1); free_vector(p); return res; } e=n-p->size; total_array_size += sizeof (struct svalue) * e; while(p->size>n) free_svalue(p->item+--(p->size)); p->size = n; return p; } } struct vector *shrink_array(struct vector *p,int n) { struct vector *res; int e; if(p->size==n) return p; if(n<=(p->malloced_size>>11)) { /* overhead to big, reallocate the array */ res = slice_array(p, 0, n-1); free_vector(p); return res; } e=n-p->size; total_array_size += sizeof (struct svalue) * e; p->size = n; return p; } static struct vector *insert_array(struct vector *v,int n,struct svalue *s) { struct vector *res; int e; #ifdef DEBUG if(n<0 || n>v->size) fatal("Illegal insert.\n"); #endif if(v->size<v->malloced_size) { /* it fits */ for(e=v->size;e>n;e--) v->item[e]=v->item[e-1]; v->size++; SET_TO_ZERO(v->item[n]); }else{ res=allocate_array_no_init(v->size+1,(v->size>>4)+1); for(e=0;e<n;e++) res->item[e]=v->item[e]; SET_TO_ZERO(res->item[e]); for(;e<v->size;e++) res->item[e+1]=v->item[e]; v->size=0; /* ugly trick */ free_vector(v); v=res; } assign_svalue(v->item+n,s); v->types |= 1<< (s->type); return v; } /* Rewritten by Profezzorn */ /* a binary string capable explode */ struct vector *explode(struct svalue *str,struct svalue *del) { int str_len,del_len,e,tmp,begin; struct vector *v; char *s,*d; s=strptr(str); d=strptr(del); str_len=my_strlen(str); del_len=my_strlen(del); if(!del_len) { v=allocate_array_no_init(str_len,0); for(e=0;e<str_len;e++) { SET_STR(v->item+e,make_shared_binary_string(s+e,1)); } }else{ for(tmp=e=0;e<=str_len-del_len;) { if(!MEMCMP(s+e,d,del_len)) { tmp++; e+=del_len; }else{ e++; } } v=allocate_array_no_init(tmp+1,0); if(!tmp) { assign_svalue_no_free(v->item,str); return v; } for(begin=tmp=e=0;e<=str_len-del_len;) { if(!MEMCMP(s+e,d,del_len)) { SET_STR(v->item+tmp,make_shared_binary_string(s+begin,e-begin)); tmp++; e+=del_len; begin=e; }else{ e++; } } SET_STR(v->item+tmp,make_shared_binary_string(s+begin,str_len-begin)); } v->types=BT_STRING; /* nothing but us strings here */ return v; } /* a binary implode */ void implode(struct svalue *to,struct vector *v,struct svalue *del) { int del_len,len,e; char *s,*s2,*del_str; del_len=my_strlen(del); del_str=strptr(del); len=del_len*(v->size-1); for(e=0;e<v->size;e++) { if(v->item[e].type!=T_STRING) continue; len+=my_strlen(v->item+e); } if(len<=0) { assign_svalue_no_free(to,&const_empty_string); }else{ s2=s=begin_shared_string(len); for(e=0;e<v->size;e++) { if(e) { MEMCPY(s2,del_str,del_len); s2+=del_len; } if(v->item[e].type!=T_STRING) continue; len=my_strlen(v->item+e); MEMCPY(s2,strptr(v->item+e),len); s2+=len; } SET_STR(to,end_shared_string(s)); /* may or may not use same area */ } } /* The following two functions are for the replace() efun.... * Profezzorn */ struct vector *array_replace(struct vector *from,struct svalue *what,struct svalue *to) { int e; struct vector *ret; check_vector_for_destruct(from); ret=allocate_array_no_init(from->size,0); for(e=0;e<from->size;e++) { if(is_eq(what,&(from->item[e]))) assign_svalue_no_free(&(ret->item[e]),to); else assign_svalue_raw(&(ret->item[e]),&(from->item[e])); } return ret; } /* * Slice of an array. */ struct vector *slice_array(struct vector *p,int from,int to) { struct vector *d; if (from < 0) from = 0; if (from >= p->size) return allocate_array(0); /* Slice starts above array */ if (to >= p->size) to = p->size-1; if (to < from) return allocate_array(0); d = allocate_array_no_init(to-from+1,0); d->types=p->types; copy_svalues_no_free(d->item,p->item+from,to-from+1); return d; } /* EFUN: filter_array Runs all elements of an array through ob->func() and returns an array holding those elements that ob->func returned 1 for. Hacking a little: if ob==0 then we call in the object in the array instead....... */ struct vector *filter(p, lambda, extra, num_extra_arg) struct vector *p; struct svalue *lambda; struct svalue *extra; int num_extra_arg; { struct vector *v,*r; int e,num; if (p->size<1) return allocate_array(0); v=map_array(p,lambda,extra,num_extra_arg); for(num=e=0;e<v->size;e++) if(v->item[e].type!=T_NUMBER || (v->item[e].u.number!=0)) num++; r=allocate_array_no_init(num,0); num=0; for(e=0;e<v->size;e++) if(v->item[e].type!=T_NUMBER || (v->item[e].u.number!=0)) assign_svalue_no_free(&r->item[num++], &p->item[e]); free_vector(v); return r; } /* Concatenation of two arrays into one */ struct vector *add_array(struct vector *p,struct vector *r) { struct vector *d; d = allocate_array_no_init(p->size+r->size,0); copy_svalues_no_free(d->item,p->item,p->size); copy_svalues_no_free(d->item+p->size,p->item,p->size); return d; } int search_alist(struct svalue *key,struct vector * keylist) { int a,b,c,d; a=0; b=keylist->size; while(a<b) { c=(a+b)>>1; d=alist_cmp(key,keylist->item+c); if(d<0) b=c; else if(d>0) a=c+1; else return c; } if(a<keylist->size && alist_cmp(key,keylist->item+a)>0) a++; return a; } int assoc(struct svalue *key,struct vector *keylist) { int a,b,c,d; a=0; b=keylist->size; while(a!=b) { c=(a+b)>>1; d=alist_cmp(key,keylist->item+c); if(d<0) b=c; else if(d>0) a=c+1; else return c; } return -1; } struct vector *subtract_array(struct vector *minuend, struct vector * subtrahend) { struct vector *vtmp; struct vector *difference; struct svalue *source,*dest; int i; vtmp=allocate_array(1); vtmp->item[0].type=T_ALIST_PART; if(subtrahend->ref==1) { vtmp->item[0].u.vec = subtrahend; subtrahend->ref++; }else{ vtmp->item[0].u.vec = slice_array(subtrahend,0,subtrahend->size); } order_alist(vtmp); subtrahend = vtmp->item[0].u.vec; difference = allocate_array(minuend->size); check_vector_for_destruct(minuend); for (source = minuend->item, dest = difference->item, i = minuend->size; i--; source++) { if ( assoc(source, subtrahend) < 0 ) assign_svalue_raw(dest++, source); } free_vector(vtmp); return shrink_array(difference, dest-difference->item); } /* Returns an array of all objects contained in 'ob' */ struct vector *all_inventory(struct object *ob) { struct vector *d; struct object *cur; int cnt,res; cnt=0; for (cur=ob->contains; cur; cur = cur->next_inv) cnt++; if (!cnt) return allocate_array(0); d = allocate_array_no_init(cnt,0); cur=ob->contains; for (res=0;res<cnt;res++) { d->item[res].type=T_OBJECT; d->item[res].u.ob = cur; add_ref(cur,"all_inventory"); cur=cur->next_inv; } return d; } /* Runs all elements of an array through ob::func and replaces each value in arr by the value returned by ob::func Hacking a little: if ob==0 then we call in the object in the array instead....... */ struct vector *map_array( struct vector *p, struct svalue *lambda, struct svalue *extra, int num_extra_arg) { struct vector *r; struct svalue *v; char *func=0; /* make gcc happy */ int cnt,no; r=0; switch(lambda->type) { case T_STRING: func=strptr(lambda); break; case T_FUNCTION: if(lambda->u.ob->flags & O_DESTRUCTED) error("Invalid lambda to map_array.\n"); break; case T_NUMBER: break; default: error("Bad arg to map_array.\n"); } if (p->size<1) return allocate_array(0); r = allocate_array(p->size); for (cnt=0;cnt<p->size;cnt++) { check_eval_cost(); switch(lambda->type) { case T_NUMBER: if(lambda->subtype!=NUMBER_NUMBER) { v=0; continue; } if(lambda->u.number<0) { if(p->item[cnt].type!=T_FUNCTION || (p->item[cnt].u.ob->flags & O_DESTRUCTED)) continue; for(no=0;no<num_extra_arg;no++) push_svalue(extra+no); v=apply_lambda(p->item+cnt,no,0); }else{ if(p->item[cnt].type!=T_OBJECT || (p->item[cnt].u.ob->flags & O_DESTRUCTED)) continue; for(no=0;no<num_extra_arg;no++) push_svalue(extra+no); v=apply_lfun(lambda->u.number,p->item[cnt].u.ob,no,0); } break; case T_STRING: if(p->item[cnt].type!=T_OBJECT || (p->item[cnt].u.ob->flags & O_DESTRUCTED)) continue; for(no=0;no<num_extra_arg;no++) push_svalue(extra+no); v=apply_shared(func,p->item[cnt].u.ob,no,0); break; case T_FUNCTION: if(lambda->u.ob->flags & O_DESTRUCTED) error("object used by map_array destructed\n"); push_svalue(&p->item[cnt]); for(no=0;no<num_extra_arg;no++) push_svalue(extra+no); v=apply_lambda(lambda,no+1,0); break; default: fatal("Fatal error in map_array.\n"); return 0; } if(v) assign_svalue_no_free(&r->item[cnt], v); } return r; } int search_array( struct vector *p, struct svalue *lambda, struct svalue *extra, int num_extra_arg) { struct svalue *v; char *func=0; /* make gcc happy */ int cnt,no; if(lambda->type==T_STRING) { func=strptr(lambda); }else if(lambda->type==T_FUNCTION){ if(lambda->u.ob->flags & O_DESTRUCTED) { error("Invalid lambda to map_array.\n"); } }else if(lambda->type==T_NUMBER){ /* no action here */ }else{ error("Bad arg to map_array.\n"); } if (p->size<1) return -1; for (cnt=0;cnt<p->size;cnt++) { check_eval_cost(); switch(lambda->type) { case T_NUMBER: if(p->item[cnt].type!=T_FUNCTION || (p->item[cnt].u.ob->flags & O_DESTRUCTED)) continue; for(no=0;no<num_extra_arg;no++) push_svalue(extra+no); v=apply_lambda(p->item+cnt,no,0); break; case T_STRING: if(p->item[cnt].type!=T_OBJECT || (p->item[cnt].u.ob->flags & O_DESTRUCTED)) continue; for(no=0;no<num_extra_arg;no++) push_svalue(extra+no); v=apply_shared(func,p->item[cnt].u.ob,no,0); break; case T_FUNCTION: if(lambda->u.ob->flags & O_DESTRUCTED) error("object used by map_array destructed\n"); push_svalue(&p->item[cnt]); for(no=0;no<num_extra_arg;no++) push_svalue(extra+no); v=apply_lambda(lambda,no+1,0); break; default: fatal("Fatal error in search_array.\n"); return 0; } if(!IS_ZERO(v)) return cnt; } return -1; } static struct svalue *func; static struct svalue *extra_arg; static int num_extra_arg; static int sort_array_cmp(struct svalue *p1,struct svalue *p2) { struct svalue *d; int e; if(!func || func->type!=T_FUNCTION) { if(p1->type>p2->type) return 1; if(p1->type<p2->type) return -1; return is_gt(p1,p2); } if (func->u.ob->flags & O_DESTRUCTED) error("object used by sort_array destructed"); push_svalue(p1); push_svalue(p2); for(e=0;e<num_extra_arg;e++) { push_svalue(extra_arg+e); } d = apply_lambda(func,num_extra_arg+2,0); if (!d) return 0; if(d->type!=T_NUMBER) return 1; return d->u.number; } typedef int (*cmpfuntyp) (const void *,const void *); struct vector *sort_array( struct vector *v, struct svalue *fun, struct svalue *extra, int num) { struct vector *outlist; int n; func=fun; extra_arg=extra; num_extra_arg=num; n=v->size; outlist=slice_array(v,0,n-1); msort(outlist->item,n,sizeof(struct svalue),(cmpfuntyp)sort_array_cmp); return outlist; } /* * deep_inventory() * * This function returns the recursive inventory of an object. The returned * array of objects is flat, ie there is no structure reflecting the * internal containment relations. * */ int number_deep_inv(struct object *ob) { int num; num=1; for(ob=ob->contains;ob;ob=ob->next_inv) num+=number_deep_inv(ob); return num; } /* This one is faster, but the array is even more scrambled compared to * the normal deep_inventory() /Profezzorn */ struct vector *deep_inventory(struct object *ob,int take_top) { int num; struct vector *res; struct svalue *curr,*next; num=number_deep_inv(ob); if(take_top) { res=allocate_array_no_init(num,0); add_ref(ob,"deep_inventory"); res->item[0].type=T_OBJECT; res->item[0].u.ob=ob; next=curr=res->item+1; }else{ res=allocate_array(num-1); curr=next=res->item; } for(;curr<=next;curr++) { for(ob=ob->contains;ob;ob=ob->next_inv) { add_ref(ob,"deep_inventory"); next->type=T_OBJECT; next->u.ob=ob; next++; } ob=curr->u.ob; } return res; } INLINE int alist_cmp(struct svalue *p1,struct svalue *p2) { register int d; if ((d = p1->type - p2->type)) return d; switch(p1->type) { case T_FLOAT: if(p1->u.fnum>p2->u.fnum) return 1; if(p1->u.fnum<p2->u.fnum) return -1; return 0; case T_FUNCTION: if ((d = p1->u.number - p2->u.number)) return d; if ((d = p1->subtype - p2->subtype)) return d; return 0; default: if(p1->u.number>p2->u.number) return 1; if(p1->u.number<p2->u.number) return -1; return 0; } } struct vector *globber; int new_alist_cmp(int *a,int *b) { return alist_cmp(globber->item+*a,globber->item+*b); } void order_alist(struct vector *inlist) { int *tmp; struct vector *tmplist,*tmptmplist; int e,d; globber=inlist->item[0].u.vec; if(!globber->size) return; tmp=(int *)malloc(globber->size*sizeof(int)); for(e=0;e<globber->size;e++) tmp[e]=e; fsort(tmp,globber->size,sizeof(int),(cmpfuntyp)new_alist_cmp); tmplist=allocate_array_no_init(globber->size,0); for(e=0;e<inlist->size;e++) { if(inlist->item[e].type==T_ALIST_PART) { tmptmplist=inlist->item[e].u.vec; for(d=0;d<tmplist->size;d++) tmplist->item[d]=tmptmplist->item[tmp[d]]; inlist->item[e].u.vec=tmplist; tmplist=tmptmplist; } } /* for(e=0;e<tmplist->size;e++) tmplist->item[e].type=T_INVALID; */ tmplist->size=0; /* instead of setting all the items in it to const0 */ free_vector(tmplist); free((char *)tmp); } /* obs, destructive /Profezzorn */ struct vector *insert_alist( struct svalue *key, struct svalue *key_data, struct vector *list) { int i,ix; int keynum; int insert; struct vector *tmp; struct svalue *sv; #if 0 for(i=0;i<list->size;i++) { if(list->item[i].type!=T_ALIST_PART) continue; sv= i ? key_data+i-1 : key; if(IS_TYPE(*sv,BT_VECTOR)) if(check_for_circularity(sv->u.vec,list)) error("Trying to make circular alist.\n"); } #endif keynum = list->item[0].u.vec->size; ix = search_alist(key,list->item[0].u.vec); insert=ix==keynum || alist_cmp(key, &list->item[0].u.vec->item[ix]); for (i=0; i < list->size; i++) { if(list->item[i].type==T_ALIST_PART) { tmp = list->item[i].u.vec; #ifdef DEBUG if(tmp->size!=keynum) fatal("If this is an alist, then I'll be buggered.\n"); #endif if(insert) { sv= i ? key_data+i-1 : key; list->item[i].u.vec=insert_array(list->item[i].u.vec,ix,sv); }else{ if(i) { sv= key_data+i-1; assign_svalue(tmp->item+ix, sv); tmp->types|=sv->type; } } } } #ifdef DEBUG for(i=0;i<list->size;i++) { if(list->item[i].type==T_ALIST_PART && list->item[0].u.vec->size!= list->item[i].u.vec->size) fatal("Hmm, this is _not_ what I had in mind....\n"); } #endif return list; } struct vector *match_regexp(struct vector *v,regexp *reg) { char *res; int i, num_match; struct vector *ret; extern int eval_cost; if (v->size == 0) return allocate_array(0); res = (char *)alloca(v->size); for (num_match=i=0; i < v->size; i++) { res[i] = 0; if (v->item[i].type != T_STRING) continue; eval_cost++; if (regexec(reg, strptr(v->item+i)) == 0) continue; res[i] = 1; num_match++; } ret = allocate_array_no_init(num_match,0); for (num_match=i=0; i < v->size; i++) { if (!res[i]) continue; assign_svalue_raw(&ret->item[num_match], &v->item[i]); num_match++; } return ret; } /* * Returns a list of all inherited files. * * Must be fixed so that any number of files can be returned, now max 256 * (Sounds like a contradiction to me /Lars). * Now it can return any number of files. /Profezzorn */ struct vector *inherit_list(struct object *ob) { struct vector *ret; struct program *pr; int e; pr=ob->prog; ret=allocate_array_no_init(pr->num_inherited,0); for(e=0;e<pr->num_inherited;e++) { SET_STR(ret->item+e,copy_shared_string(pr->inherit[e].prog->name)); } return ret; } /* * When a vector is given as argument to an efun, all items has to be * checked if there would be an destructed object. * A bad problem currently is that a vector can contain another vector, so this * should be tested too. But, there is currently no prevention against * recursive vectors, which means that this can not be tested. Thus, the game * may crash if a vector contains a vector that contains a destructed object * and this top-most vector is used as an argument to an efun. */ /* The game won't crash when doing simple operations like assign_svalue * on a destructed object. You have to watch out, of course, that you don't * apply a function to it. * to save space it is preferable that destructed objects are freed soon. * amylaar */ void donk_alist_item(struct vector *m,int i) { int e,d,s; for(s=e=0;e<m->size;e++) { if(m->item[e].type!=T_ALIST_PART) continue; free_svalue(m->item[e].u.vec->item+i); for(d=i+1;d<m->item[e].u.vec->size;d++) m->item[e].u.vec->item[d-1]=m->item[e].u.vec->item[d]; m->item[e].u.vec->size--; } total_array_size -= s*sizeof (struct svalue); } /* I'm a big beleiver in lazy evaluation algorithms */ void check_vector_for_destruct(struct vector *v) { int types,e; /* if there are no objects or function, why check? */ if(!(v->types & (BT_OBJECT | BT_FUNCTION))) return; types=0; for(e=v->size-1;e>=0;e--) { if(IS_TYPE(v->item[e],BT_OBJECT | BT_FUNCTION)) { if(v->item[e].u.ob->flags & O_DESTRUCTED) { short tmp; tmp=v->item[e].type=T_OBJECT; free_svalue(v->item+e); v->item[e].subtype=tmp?NUMBER_DESTRUCTED_OBJECT:NUMBER_DESTRUCTED_FUNCTION; } } types|=1<<v->item[e].type; } v->types=types; } /* { 0,1,1,0,0,1 } { 1,0,0,1,1,0 } */ void check_alist_for_destruct(struct vector *v) { int e,types; struct vector *w; check_vector_for_destruct(v); w=v->item[0].u.vec; if(w->types & (BT_OBJECT | BT_FUNCTION)) { types=0; for(e=0;e<w->size;e++) { if(IS_TYPE(w->item[e],BT_OBJECT | BT_FUNCTION) && (w->item[e].u.ob->flags & O_DESTRUCTED)) { donk_alist_item(v,e); e--; }else{ types|=1<<w->item[e].type; } } w->types=types; } for(e=1;e<v->size;e++) if(v->item[e].type==T_ALIST_PART) check_vector_for_destruct(v->item[e].u.vec); } /* check if v2 is in v */ int check_for_circularity(struct vector *v,struct vector *v2) { int types,e; if(v==v2) return 1; /* if there are no vectors, why search? */ if(!(v->types & BT_VECTOR)) return 0; types=0; for(e=v->size-1;e>=0;e--) { if(IS_TYPE(v->item[e],BT_VECTOR)) if(check_for_circularity(v->item[e].u.vec,v2)) return 1; types|=1<<v->item[e].type; } /* if we reach this, types is updated. * as recursive arrays gives errors, we should come here * almost every time. */ v->types=types; return 0; } struct vector *allocate_n_array(struct svalue *sp,int t) { struct vector *v; int e; extern int eval_cost; if(sp->type!=T_NUMBER) error("Non-numeric array dimension.\n"); v=allocate_array(sp->u.number); eval_cost+=sp->u.number*AVERAGE_COST; t--; v->types=BT_NUMBER; if(!t) return v; sp++; v->types=BT_POINTER; for(e=0;e<v->size && eval_cost<MAX_COST;e++) { v->item[e].type=T_POINTER; v->item[e].u.vec=allocate_n_array(sp,t); } return v; } struct vector *sum_arrays( int num_of_arrays, struct svalue *arrays, struct svalue *fun) { int d,e,size; struct vector *res; struct svalue *ret; size=arrays->u.vec->size; res=allocate_array_no_init(size,0); for(e=0;e<size;e++) { for(d=0;d<num_of_arrays;d++) { if(arrays[d].u.vec->size>e) push_svalue(& arrays[d].u.vec->item[e]); else push_number(0); } if(fun->u.ob->flags & O_DESTRUCTED) { free_vector(res); error("Object used by sum_arrays destructed.\n"); } ret=apply_lambda(fun,num_of_arrays,0); if(!ret) { free_vector(res); error("Function used by sum_arrays() not found.\n"); } assign_svalue_no_free(& res->item[e],ret); } return res; } struct vector *file_stat(char *file,int raw) { struct vector *tmp; struct stat stbuf; char *p; p = file; if(!batch_mode) while(*p && *p == '/') p++; if(raw) { if(lstat(p, &stbuf) == -1) return 0; }else{ if(stat(p, &stbuf) == -1) return 0; } tmp = allocate_array(5); tmp->item[0].u.number = stbuf.st_mode; tmp->item[1].u.number = (S_IFDIR == (S_IFMT & stbuf.st_mode)) ? -2 : (S_IFLNK == (S_IFMT & stbuf.st_mode)) ? -3 : stbuf.st_size; tmp->item[2].u.number = stbuf.st_atime; tmp->item[3].u.number = stbuf.st_mtime; tmp->item[4].u.number = stbuf.st_ctime; tmp->types=BT_NUMBER; return tmp; }