/* -*- c -*- |
* $Id: builtin.cmod,v 1.83 2002/04/08 01:17:05 mast Exp $ |
*/ |
|
#include "global.h" |
#include "interpret.h" |
#include "svalue.h" |
#include "opcodes.h" |
#include "pike_macros.h" |
#include "object.h" |
#include "program.h" |
#include "array.h" |
#include "pike_error.h" |
#include "constants.h" |
#include "mapping.h" |
#include "stralloc.h" |
#include "multiset.h" |
#include "pike_types.h" |
#include "pike_memory.h" |
#include "threads.h" |
#include <math.h> |
#include <ctype.h> |
#include "module_support.h" |
#include "cyclic.h" |
#include "bignum.h" |
#include "main.h" |
#include "operators.h" |
#include "builtin_functions.h" |
#include "fsort.h" |
#include "port.h" |
|
|
/*! @decl array(array(int|string)) _describe_program(program p) |
*! |
*! Debug function for showing the symbol table of a program. |
*/ |
PIKEFUN array(array(int|string)) _describe_program(mixed x) |
efun; |
{ |
struct program *p; |
struct array *res; |
int i; |
|
if (!(p = program_from_svalue(sp - args))) { |
Pike_error("_describe_program(): Bad argument 1 (expected program).\n"); |
} |
|
for (i=0; i < (int)p->num_identifier_references;i++) { |
struct reference *ref = p->identifier_references + i; |
struct identifier *id = ID_FROM_PTR(p, ref); |
push_int(ref->id_flags); |
ref_push_string(id->name); |
f_aggregate(2); |
} |
f_aggregate(p->num_identifier_references); |
res = sp[-1].u.array; |
sp--; |
pop_n_elems(args); |
push_array(res); |
} |
|
/*! @decl string basetype(mixed x) |
*! |
*! Same as sprintf("%t",x); |
*! |
*! @seealso |
*! @[sprintf()] |
*/ |
PIKEFUN string basetype(mixed x) |
efun; |
optflags OPT_TRY_OPTIMIZE; |
{ |
int t=x->type; |
if(x->type == T_OBJECT && x->u.object->prog) |
{ |
ptrdiff_t fun=FIND_LFUN(x->u.object->prog, LFUN__SPRINTF); |
if(fun != -1) |
{ |
push_int('t'); |
f_aggregate_mapping(0); |
apply_low(x->u.object, fun, 2); |
if(Pike_sp[-1].type == T_STRING) |
{ |
stack_swap(); |
pop_stack(); |
return; |
} else if (UNSAFE_IS_ZERO(Pike_sp-1)) { |
pop_n_elems(2); |
push_constant_text("object"); |
return; |
} else { |
Pike_error("Non-string returned from _sprintf()\n"); |
} |
} |
} |
pop_stack(); |
switch(t) |
{ |
case T_ARRAY: push_constant_text("array"); break; |
case T_FLOAT: push_constant_text("float"); break; |
case T_FUNCTION: push_constant_text("function"); break; |
case T_INT: push_constant_text("int"); break; |
case T_LVALUE: push_constant_text("lvalue"); break; |
case T_MAPPING: push_constant_text("mapping"); break; |
case T_MULTISET: push_constant_text("multiset"); break; |
case T_OBJECT: push_constant_text("object"); break; |
case T_PROGRAM: push_constant_text("program"); break; |
case T_STRING: push_constant_text("string"); break; |
case T_TYPE: push_constant_text("type"); break; |
case T_ZERO: push_constant_text("zero"); break; |
case T_VOID: push_constant_text("void"); break; |
case T_MAPPING_DATA: push_constant_text("mapping_data"); break; |
default: push_constant_text("unknown"); break; |
} |
} |
|
|
/*! @decl string int2char(int x) |
*! @appears String.int2char |
*! |
*! Same as sprintf("%c",x); |
*! |
*! @seealso |
*! @[sprintf()] |
*/ |
PIKEFUN string int2char(int|object x) |
efun; |
optflags OPT_TRY_OPTIMIZE; |
{ |
int c; |
if(x->type == T_OBJECT && x->u.object->prog) |
{ |
ptrdiff_t fun=FIND_LFUN(x->u.object->prog, LFUN__SPRINTF); |
if(fun != -1) |
{ |
push_int('c'); |
f_aggregate_mapping(0); |
apply_low(x->u.object, fun, 2); |
if(Pike_sp[-1].type == T_STRING) |
{ |
stack_swap(); |
pop_stack(); |
return; |
} |
Pike_error("Non-string returned from _sprintf()\n"); |
} |
} |
if(x->type != T_INT) |
Pike_error("Bad argument 1 to int2char.\n"); |
|
c=x->u.integer; |
|
if(c>=0 && c<256) |
{ |
struct pike_string *s; |
s=begin_shared_string(1); |
s->str[0]=c; |
RETURN end_shared_string(s); |
}else{ |
struct string_builder tmp; |
init_string_builder(&tmp,0); |
string_builder_putchar(&tmp, c); |
RETURN finish_string_builder(&tmp); |
} |
} |
|
/*! @decl string int2hex(int x) |
*! @appears String.int2hex |
*! |
*! Same as sprintf("%x",x); |
*! |
*! @seealso |
*! @[sprintf()] |
*/ |
PIKEFUN string int2hex(int|object x) |
efun; |
optflags OPT_TRY_OPTIMIZE; |
{ |
INT_TYPE c; |
unsigned long n; |
int len; |
struct pike_string *s; |
|
if(x->type == T_OBJECT && x->u.object->prog) |
{ |
ptrdiff_t fun=FIND_LFUN(x->u.object->prog, LFUN__SPRINTF); |
if(fun != -1) |
{ |
push_int('x'); |
f_aggregate_mapping(0); |
apply_low(x->u.object, fun, 2); |
if(Pike_sp[-1].type == T_STRING) |
{ |
stack_swap(); |
pop_stack(); |
return; |
} |
Pike_error("Non-string returned from _sprintf()\n"); |
} |
} |
if(x->type != T_INT) |
Pike_error("Bad argument 1 to int2hex.\n"); |
|
c=x->u.integer; |
|
len=1; |
if(c<0) { |
len++; |
n=(-c)&((unsigned int)(-1)); |
}else{ |
n=c; |
} |
while(n>65535) { n>>=16; len+=4; } |
while(n>15) { n>>=4; len++; } |
|
s=begin_shared_string(len); |
c=x->u.integer; |
if(!c) |
{ |
s->str[0]='0'; |
}else{ |
if(c<0) |
{ |
s->str[0]='-'; |
n=(-c)&((unsigned int)(-1)); |
}else{ |
n=c; |
} |
while(len && n) |
{ |
s->str[--len]="0123456789abcdef"[n&0xf]; |
n>>=4; |
} |
} |
RETURN end_shared_string(s); |
} |
|
/*! @decl array column(array data, mixed index) |
*! |
*! Extract a column from a two-dimensional array. |
*! |
*! This function is exactly equivalent to: |
*! @code{map(@[data], lambda(mixed x,mixed y) { return x[y]; }, @[index])@} |
*! |
*! Except of course it is a lot shorter and faster. |
*! That is, it indices every index in the array data on the value of |
*! the argument index and returns an array with the results. |
*! |
*! @seealso |
*! @[rows()] |
*/ |
PIKEFUN array column(array data, mixed index) |
efun; |
optflags OPT_TRY_OPTIMIZE; |
{ |
INT32 e; |
struct array *a; |
|
DECLARE_CYCLIC(); |
|
/* Optimization */ |
if(data->refs == 1) |
{ |
/* An array with one ref cannot possibly be cyclic */ |
struct svalue sval; |
data->type_field = BIT_MIXED | BIT_UNFINISHED; |
for(e=0;e<data->size;e++) |
{ |
index_no_free(&sval, ITEM(data)+e, index); |
free_svalue(ITEM(data)+e); |
ITEM(data)[e]=sval; |
} |
pop_stack(); |
return; |
} |
|
if((a=(struct array *)BEGIN_CYCLIC(data,0))) |
{ |
add_ref(a); |
}else{ |
push_array(a=allocate_array(data->size)); |
SET_CYCLIC_RET(a); |
|
for(e=0;e<a->size;e++) |
index_no_free(ITEM(a)+e, ITEM(data)+e, index); |
|
sp--; |
} |
END_CYCLIC(); |
RETURN a; |
} |
|
/*! @decl multiset mkmultiset(array a) |
*! |
*! This function creates a multiset from an array. |
*! |
*! @seealso |
*! @[aggregate_multiset()] |
*! |
*/ |
PIKEFUN multiset(1) mkmultiset(array(1=mixed) a) |
efun; |
optflags OPT_TRY_OPTIMIZE|OPT_EXTERNAL_DEPEND; |
{ |
RETURN mkmultiset(a); |
} |
|
/*! @decl int trace(int t) |
*! |
*! This function changes the debug trace level. |
*! |
*! The old level is returned. |
*! |
*! Trace level 1 or higher means that calls to Pike functions are |
*! printed to stderr, level 2 or higher means calls to builtin functions |
*! are printed, 3 means every opcode interpreted is printed, 4 means |
*! arguments to these opcodes are printed as well. |
*! |
*! See the @tt{-t@} command-line option for more information. |
*/ |
PIKEFUN int trace(int t) |
efun; |
optflags OPT_SIDE_EFFECT; |
{ |
pop_n_elems(args); |
push_int(t_flag); |
t_flag=t; |
} |
|
/*! @decl string ctime(int timestamp) |
*! |
*! Convert the output from a previous call to @[time()] into a readable |
*! string containing the current year, month, day and time. |
*! |
*! @seealso |
*! @[time()], @[localtime()], @[mktime()], @[gmtime()] |
*/ |
PIKEFUN string ctime(int timestamp) |
efun; |
optflags OPT_TRY_OPTIMIZE; |
{ |
time_t i=(time_t)timestamp; |
RETURN make_shared_string(ctime(&i)); |
} |
|
/*! @decl mapping mkmapping(array ind, array val) |
*! |
*! Make a mapping from two arrays. |
*! |
*! Makes a mapping @[ind[x]]:@[val[x]], @tt{0 <= x < sizeof(ind)@}. |
*! |
*! @[ind] and @[val] must have the same size. |
*! |
*! This is the inverse operation of @[indices()] and @[values()]. |
*! |
*! @seealso |
*! @[indices()], @[values()] |
*/ |
PIKEFUN mapping(1:2) mkmapping(array(1=mixed) ind, array(2=mixed) val) |
efun; |
optflags OPT_TRY_OPTIMIZE|OPT_EXTERNAL_DEPEND; |
{ |
if(ind->size != val->size) |
bad_arg_error("mkmapping", sp-args, args, 2, "array", sp+1-args, |
"mkmapping called on arrays of different sizes (%d != %d)\n", |
ind->size, val->size); |
|
RETURN mkmapping(ind, val); |
} |
|
/*! @decl int count(string haystack, string needle) |
*! @belongs String |
*! |
*! Count the number of non-overlapping times the string @[needle] occurrs |
*! in the string @[haystack]. |
*! |
*! @seealso |
*! @[search()], @[`/()] |
*/ |
PIKEFUN int string_count(string haystack, string needle) |
errname String.count; |
optflags OPT_TRY_OPTIMIZE; |
{ |
ptrdiff_t c = 0; |
ptrdiff_t i, j; |
|
switch (needle->len) |
{ |
case 0: |
switch (haystack->len) |
{ |
case 0: c=1; break; /* "" appears one time in "" */ |
case 1: c=0; break; /* "" doesn't appear in "x" */ |
default: c=haystack->len-1; /* one time between each character */ |
} |
break; |
case 1: |
/* maybe optimize? */ |
default: |
for (i=0; i<haystack->len; i++) |
{ |
j=string_search(haystack,needle,i); |
if (j==-1) break; |
i=j+needle->len-1; |
c++; |
} |
break; |
} |
RETURN DO_NOT_WARN((INT_TYPE)c); |
} |
|
/*! @decl string trim_whites (string s) |
*! @belongs String |
*! |
*! Trim leading and trailing spaces and tabs from the string @[s]. |
*/ |
PIKEFUN string string_trim_whites (string s) |
errname String.trim_whites; |
optflags OPT_TRY_OPTIMIZE; |
{ |
ptrdiff_t start = 0, end = s->len; |
int chr; |
switch (s->size_shift) { |
#define DO_IT(TYPE) \ |
{ \ |
for (; start < s->len; start++) { \ |
chr = ((TYPE *) s->str)[start]; \ |
if (chr != ' ' && chr != '\t') break; \ |
} \ |
while (--end > start) { \ |
chr = ((TYPE *) s->str)[end]; \ |
if (chr != ' ' && chr != '\t') break; \ |
} \ |
} |
case 0: DO_IT (p_wchar0); break; |
case 1: DO_IT (p_wchar1); break; |
case 2: DO_IT (p_wchar2); break; |
#undef DO_IT |
} |
RETURN string_slice (s, start, end + 1 - start); |
} |
|
/*! @decl string trim_all_whites (string s) |
*! @belongs String |
*! |
*! Trim leading and trailing white spaces characters (space, tab, |
*! newline and carriage return) from the string @[s]. |
*/ |
PIKEFUN string string_trim_all_whites (string s) |
errname String.trim_all_whites; |
optflags OPT_TRY_OPTIMIZE; |
{ |
ptrdiff_t start = 0, end = s->len; |
int chr; |
switch (s->size_shift) { |
#define DO_IT(TYPE) \ |
{ \ |
for (; start < s->len; start++) { \ |
chr = ((TYPE *) s->str)[start]; \ |
if (chr != ' ' && chr != '\t' && chr != '\n' && chr != '\r') \ |
break; \ |
} \ |
while (--end > start) { \ |
chr = ((TYPE *) s->str)[end]; \ |
if (chr != ' ' && chr != '\t' && chr != '\n' && chr != '\r') \ |
break; \ |
} \ |
} |
case 0: DO_IT (p_wchar0); break; |
case 1: DO_IT (p_wchar1); break; |
case 2: DO_IT (p_wchar2); break; |
#undef DO_IT |
} |
RETURN string_slice (s, start, end + 1 - start); |
} |
|
/*! @decl int implements(program prog, program api) |
*! @belongs Program |
*! |
*! Returns 1 if @[prog] implements @[api]. |
*/ |
PIKEFUN int program_implements(program prog, program api) |
errname Program.implements; |
optflags OPT_TRY_OPTIMIZE; |
{ |
RETURN implements(prog, api); |
} |
|
/*! @decl int inherits(program child, program parent) |
*! @belongs Program |
*! |
*! Returns 1 if @[child] has inherited @[parent]. |
*/ |
PIKEFUN int program_inherits(program parent, program child) |
errname Program.inherits; |
optflags OPT_TRY_OPTIMIZE; |
{ |
RETURN low_get_storage(parent, child) != -1; |
} |
|
/*! @decl string defined(program p) |
*! @belongs Program |
*! |
*! Returns a string with filename and linenumber describing where |
*! the program @[p] was defined. |
*! |
*! The returned string is of the format @tt{"@i{filename@}:@i{linenumber@}"@}. |
*! |
*! If it cannot be determined where the program was defined, @tt{0@} (zero) |
*! will be returned. |
*/ |
PIKEFUN string program_defined(program p) |
errname Program.defined; |
optflags OPT_TRY_OPTIMIZE; |
{ |
if(p && p->num_linenumbers) |
{ |
INT32 line; |
struct pike_string *tmp = get_program_line(p, &line); |
|
pop_n_elems(args); |
|
push_string(tmp); |
if(line >= 1) |
{ |
push_constant_text(":"); |
push_int(line); |
f_add(3); |
} |
return; |
} |
|
pop_n_elems(args); |
push_int(0); |
} |
|
/*! @decl int(8..8)|int(16..16)|int(32..32) width(string s) |
*! @belongs String |
*! |
*! Returns the width of a string. |
*! |
*! Three return values are possible: |
*! @int |
*! @value 8 |
*! The string @[s] only contains characters <= 255. |
*! @value 16 |
*! The string @[s] only contains characters <= 65535. |
*! @value 32 |
*! The string @[s] contains characters >= 65536. |
*! @endint |
*/ |
PIKEFUN int(8 .. 8)|int(16 .. 16)|int(32 .. 32) string_width(string s) |
errname String.width; |
optflags OPT_TRY_OPTIMIZE; |
{ |
RETURN 8 * (1 << s->size_shift); |
} |
|
/*! @decl mixed m_delete(object|mapping map, mixed index) |
*! |
*! If @[map] is an object that implements @[lfun::_m_delete()], |
*! that function will be called with @[index] as the signle argument. |
*! |
*! Other wise if @[map] is a mapping the entry with index @[index] |
*! will be removed from @[map] destructively. |
*! |
*! If the mapping does not have an entry with index @[index], nothing is done. |
*! |
*! @returns |
*! The value that was removed will be returned. |
*! |
*! @note |
*! Note that @[m_delete()] changes @[map] destructively. |
*! |
*! @seealso |
*! @[mappingp()] |
*/ |
PIKEFUN mixed m_delete(object|mapping map, mixed index) |
efun; |
optflags OPT_SIDE_EFFECT; |
{ |
/*FIXME: Should be |
* type function(mapping(1=mixed:2=mixed),1:2)| |
* function(object,mixed:mixed); |
* |
* or similar |
*/ |
if( map->type == T_MAPPING ) |
{ |
struct svalue s; |
map_delete_no_free(map->u.mapping, index, &s); |
pop_n_elems(args); |
*sp=s; |
sp++; |
} |
else if (map->type == T_OBJECT && map->u.object->prog) |
{ |
int id = FIND_LFUN(map->u.object->prog, LFUN__M_DELETE); |
|
if( id == -1 ) |
SIMPLE_BAD_ARG_ERROR("m_delete", 1, "object with _m_delete"); |
|
apply_low( map->u.object, id, 1 ); |
stack_swap(); |
pop_stack(); |
} else { |
SIMPLE_BAD_ARG_ERROR("m_delete", 1, "object|mapping"); |
} |
} |
|
/*! @decl int get_weak_flag(array|mapping|multiset m) |
*! |
*! Returns the weak flag settings for @[m]. It's a combination of |
*! @[Pike.WEAK_INDICES] and @[Pike.WEAK_VALUES]. |
*/ |
PIKEFUN int get_weak_flag(array m) |
efun; |
optflags OPT_EXTERNAL_DEPEND; |
{ |
RETURN (m->flags & ARRAY_WEAK_FLAG) ? PIKE_WEAK_VALUES : 0; |
} |
|
PIKEFUN int get_weak_flag(mapping m) |
{ |
RETURN mapping_get_flags(m) & MAPPING_WEAK; |
} |
|
PIKEFUN int get_weak_flag(multiset m) |
{ |
#ifdef PIKE_NEW_MULTISETS |
RETURN multiset_get_flags(m) & MULTISET_WEAK; |
#else |
RETURN (m->ind->flags & (ARRAY_WEAK_FLAG|ARRAY_WEAK_SHRINK)) ? |
PIKE_WEAK_INDICES : 0; |
#endif |
} |
|
PIKEFUN program __empty_program() |
efun; |
optflags OPT_EXTERNAL_DEPEND; |
{ |
RETURN low_allocate_program(); |
} |
|
/*! @decl string function_name(function f) |
*! |
*! Return the name of the function @[f]. |
*! |
*! If @[f] is a global function defined in the runtime @tt{0@} (zero) |
*! will be returned. |
*! |
*! @seealso |
*! @[function_object()] |
*/ |
PIKEFUN string function_name(program|function func) |
efun; |
optflags OPT_TRY_OPTIMIZE; |
{ |
switch(func->type) |
{ |
default: |
bad_arg_error("function_name", Pike_sp-args, args, 1, |
"function|program", Pike_sp-args, |
"Bad argument.\n"); |
return; /* NOTREACHED */ |
|
case PIKE_T_PROGRAM: |
{ |
struct program *p=func->u.program; |
|
if(p->parent) |
{ |
int e; |
p=p->parent; |
/* search constants in parent for this |
* program... |
*/ |
|
for(e = p->num_identifier_references; e--; ) |
{ |
struct identifier *id; |
if (p->identifier_references[e].id_flags & ID_HIDDEN) |
continue; |
|
id = ID_FROM_INT(p, e); |
if (IDENTIFIER_IS_CONSTANT(id->identifier_flags) && |
is_eq( & PROG_FROM_INT(p, e)->constants[id->func.offset].sval, |
func)) |
REF_RETURN id->name; |
} |
} |
break; |
} |
|
case PIKE_T_FUNCTION: |
if(func->subtype == FUNCTION_BUILTIN) break; |
if(!func->u.object->prog) |
bad_arg_error("function_name", Pike_sp-args, args, 1, |
"function", Pike_sp-args, |
"Destructed object.\n"); |
if(func->u.object->prog == pike_trampoline_program) |
{ |
struct pike_trampoline *t; |
t=((struct pike_trampoline *)func->u.object->storage); |
if(t->frame->current_object->prog) |
REF_RETURN ID_FROM_INT(t->frame->current_object->prog, |
t->func)->name; |
} |
|
REF_RETURN ID_FROM_INT(func->u.object->prog, func->subtype)->name; |
} |
pop_n_elems(args); |
push_int(0); |
} |
|
/*! @decl object function_object(function|program f) |
*! |
*! Return the object the function @[f] is in. |
*! |
*! If @[f] is a global function defined in the runtime @tt{0@} (zero) |
*! will be returned. |
*! |
*! @seealso |
*! @[function_name()] |
*/ |
PIKEFUN object|program function_object(object|program|function func) |
efun; |
optflags OPT_TRY_OPTIMIZE; |
type function(function|object:object)|function(program:program); |
{ |
switch(func->type) |
{ |
case PIKE_T_PROGRAM: |
{ |
struct program *p; |
if(!(p=func->u.program->parent)) break; |
add_ref(p); |
free_program(func->u.program); |
func->u.program=p; |
return; |
} |
|
case PIKE_T_FUNCTION: |
if(func->subtype == FUNCTION_BUILTIN) break; |
if(func->u.object->prog == pike_trampoline_program) |
{ |
struct object *o; |
o=((struct pike_trampoline *)func->u.object->storage)->frame->current_object; |
add_ref(o); |
pop_n_elems(args); |
push_object(o); |
return; |
} |
func->type=T_OBJECT; |
return; |
|
|
default: |
SIMPLE_BAD_ARG_ERROR("function_object",1,"function"); |
} |
pop_n_elems(args); |
push_int(0); |
} |
|
|
|
/*! @decl int random(int max) |
*! |
*! This function returns a random number in the range 0 - @[max]-1. |
*! |
*! @seealso |
*! @[random_seed()] |
*/ |
|
|
PIKEFUN mixed random(object o) |
efun; |
optflags OPT_TRY_OPTIMIZE|OPT_EXTERNAL_DEPEND; |
{ |
apply(o,"_random",0); |
stack_swap(); |
pop_stack(); |
} |
|
PIKEFUN int random(int i) |
{ |
if(i <= 0) RETURN 0; |
RETURN my_rand() % i; |
} |
|
PIKEFUN float random(float f) |
{ |
if(f<=0.0) RETURN 0.0; |
#define N 1048576 |
RETURN f * (my_rand()%N/((float)N)) + |
f * (my_rand()%N/( ((float)N) * ((float)N) )); |
|
} |
|
PIKEFUN mixed random(array a) |
{ |
if(!a->size) |
SIMPLE_BAD_ARG_ERROR("random", 1, "array with elements in it"); |
push_svalue(a->item + (my_rand() % a->size)); |
stack_swap(); |
pop_stack(); |
} |
|
PIKEFUN mixed random(multiset m) |
{ |
if(multiset_is_empty (m)) |
SIMPLE_BAD_ARG_ERROR("random", 1, "multiset with elements in it"); |
#ifdef PIKE_NEW_MULTISETS |
if (multiset_indval (m)) { |
ptrdiff_t nodepos = multiset_get_nth (m, my_rand() % multiset_sizeof (m)); |
push_multiset_index (m, nodepos); |
push_multiset_value (m, nodepos); |
sub_msnode_ref (m); |
f_aggregate (2); |
} |
else { |
push_multiset_index (m, multiset_get_nth (m, my_rand() % multiset_sizeof (m))); |
sub_msnode_ref (m); |
} |
#else |
push_svalue(m->ind->item + (my_rand() % m->ind->size)); |
#endif |
stack_swap(); |
pop_stack(); |
} |
|
PIKEFUN array random(mapping m) |
{ |
struct mapping_data *md=m->data; |
size_t bucket, count; |
struct keypair *k; |
|
if(!m_sizeof(m)) |
SIMPLE_BAD_ARG_ERROR("random", 1, "mapping with elements in it"); |
|
/* Find a random, nonempty bucket */ |
bucket=my_rand() % md->hashsize; |
while(! md->hash[bucket] ) |
if(++bucket > (size_t)md->hashsize) |
bucket=0; |
|
/* Count entries in bucket */ |
count=0; |
for(k=md->hash[bucket];k;k=k->next) count++; |
|
/* Select a random entry in this bucket */ |
count = my_rand() % count; |
k=md->hash[bucket]; |
while(count-- > 0) k=k->next; |
|
/* Push result and return */ |
push_svalue(&k->ind); |
push_svalue(&k->val); |
f_aggregate(2); |
stack_swap(); |
pop_stack(); |
} |
|
/* |
* Backtrace handling. |
*/ |
|
/*! @module Pike |
*/ |
|
/*! @class BacktraceFrame |
*/ |
|
PIKECLASS backtrace_frame |
{ |
PIKEVAR mixed fun; |
PIKEVAR array args; |
CVAR struct program *prog; /* FIXME: Ought to be a private pikevar... */ |
CVAR PIKE_OPCODE_T *pc; |
CVAR struct pike_string *filename; |
CVAR INT_TYPE lineno; |
|
INIT |
{ |
THIS->fun.type = T_INT; |
THIS->fun.u.integer = 0; |
THIS->prog = NULL; |
THIS->pc = NULL; |
THIS->lineno = 0; |
THIS->args = NULL; |
THIS->filename = NULL; |
} |
|
EXIT |
{ |
if (THIS->prog) { |
free_program(THIS->prog); |
THIS->prog = NULL; |
} |
if (THIS->args) { |
free_array(THIS->args); |
THIS->args = NULL; |
} |
if (THIS->filename) { |
free_string(THIS->filename); |
THIS->filename = NULL; |
} |
THIS->pc = NULL; |
THIS->lineno = 0; |
free_svalue(&THIS->fun); |
THIS->fun.type = T_INT; |
THIS->fun.u.integer = 0; |
} |
|
PIKEFUN int(0..1) _is_type(string t) |
{ |
INT_TYPE res = (t == findstring("array")); |
pop_n_elems(args); |
push_int(res); |
} |
|
PIKEFUN string _sprintf(int c, mapping|void opts) |
{ |
pop_n_elems(args); |
|
push_text("backtrace_frame("); |
if (THIS->pc) { |
if (!THIS->filename) { |
THIS->filename = get_line(THIS->pc, THIS->prog, &THIS->lineno); |
} |
THIS->pc = NULL; |
} |
if (THIS->prog) { |
free_program(THIS->prog); |
THIS->prog = NULL; |
} |
if (THIS->filename) { |
ref_push_string(THIS->filename); |
push_text(":"); |
push_int(THIS->lineno); |
push_text(", "); |
f_add(4); |
} else { |
push_text("Unknown file, "); |
} |
if (THIS->fun.type == PIKE_T_FUNCTION) { |
if (THIS->fun.u.object->prog) { |
push_svalue(&THIS->fun); |
f_function_name(1); |
push_text("(), "); |
f_add(2); |
} else { |
free_svalue(&THIS->fun); |
THIS->fun.type = PIKE_T_INT; |
THIS->fun.u.integer = 0; |
THIS->fun.subtype = NUMBER_DESTRUCTED; |
push_text("destructed_function(), "); |
} |
} else { |
push_text("destructed_function(), "); |
} |
|
if (THIS->args) { |
push_text("Args: "); |
push_int(THIS->args->size); |
f_add(2); |
} else { |
push_text("No args"); |
} |
push_text(")"); |
f_add(5); |
} |
|
PIKEFUN int _sizeof() |
{ |
if (THIS->args) { |
push_int(THIS->args->size + 3); |
} else { |
push_int(3); |
} |
} |
|
PIKEFUN mixed `[](int index, int|void end_or_none) |
{ |
INT_TYPE end = index; |
INT32 numargs = 0; |
INT32 i; |
|
if (THIS->args) { |
numargs = THIS->args->size; |
} |
|
numargs += 3; |
|
if (!end_or_none) { |
if (index < 0) { |
index_error("pike_frame->`[]", Pike_sp-args, args, NULL, Pike_sp-args, |
"Indexing with negative index (%"PRINTPIKEINT"d)\n", index); |
} else if (index >= numargs) { |
index_error("pike_frame->`[]", Pike_sp-args, args, NULL, Pike_sp-args, |
"Indexing with too large index (%"PRINTPIKEINT"d)\n", index); |
} |
} else { |
if (end_or_none->type != PIKE_T_INT) { |
SIMPLE_BAD_ARG_ERROR("`[]",2,"int|void"); |
} |
end = end_or_none->u.integer; |
} |
|
pop_n_elems(args); |
|
if (end_or_none) { |
if ((end < 0) || (end < index) || (index >= numargs)) { |
f_aggregate(0); |
return; |
} |
|
if (end >= numargs) { |
end = numargs-1; |
} |
} |
|
for (i = index; i <= end; i++) { |
switch(i) { |
case 0: /* Filename */ |
case 1: /* Linenumber */ |
if (THIS->pc) { |
if (!THIS->filename) { |
THIS->filename = get_line(THIS->pc, THIS->prog, &THIS->lineno); |
} |
THIS->pc = NULL; |
} |
if (THIS->prog) { |
free_program(THIS->prog); |
THIS->prog = NULL; |
} |
if (i) { |
/* Linenumber */ |
push_int(THIS->lineno); |
} else { |
/* Filename */ |
if (THIS->filename) { |
ref_push_string(THIS->filename); |
} else { |
push_int(0); |
} |
} |
break; |
case 2: /* Function */ |
push_svalue(&THIS->fun); |
break; |
default: /* Arguments */ |
{ |
if ((i > 2) && (THIS->args) && (i-3 < THIS->args->size)) { |
push_svalue(THIS->args->item + (i - 3)); |
break; |
} |
bad_arg_error("backtrace_frame->`[]", Pike_sp-args, args, 1, |
"int(0..)", Pike_sp-args, |
"Bad argument 1 to backtrace_frame->`[](): " |
"Expected int(0..%d)\n", |
numargs + 2); |
} |
/* NOT_REACHED */ |
break; |
} |
} |
if (end_or_none) { |
f_aggregate(1 + end - index); |
} |
} |
|
PIKEFUN mixed `[]=(int index, mixed value) |
{ |
INT32 numargs = 0; |
|
if (THIS->args) { |
numargs = THIS->args->size; |
} |
|
numargs += 3; |
|
if ((index < -numargs) || (index >= numargs)) { |
index_error("pike_frame->`[]=", Pike_sp-args, args, NULL, Pike_sp-args, |
"Index %"PRINTPIKEINT"d is out of array range 0 - %d,\n", |
index, numargs-1); |
} else if (index < 0) { |
index += numargs; |
} |
|
if (args > 2) { |
pop_n_elems(args - 2); |
args = 2; |
} |
|
switch(index) { |
case 0: /* Filename */ |
case 1: /* Linenumber */ |
/* First make sure we have line-number info. */ |
if (THIS->pc) { |
if (!THIS->filename) { |
THIS->filename = get_line(THIS->pc, THIS->prog, &THIS->lineno); |
} |
THIS->pc = NULL; |
} |
if (THIS->prog) { |
free_program(THIS->prog); |
THIS->prog = NULL; |
} |
if (index) { |
/* Linenumber */ |
if (value->type != PIKE_T_INT) { |
SIMPLE_BAD_ARG_ERROR("backtrace_frame->`[]=", 2, "int(1..)"); |
} |
THIS->lineno = value->u.integer; |
} else { |
/* Filename */ |
if (value->type != PIKE_T_STRING) { |
if ((value->type != PIKE_T_INT) || |
(value->u.integer)) { |
SIMPLE_BAD_ARG_ERROR("backtrace_frame->`[]=", 2, |
"string|int(0..0)"); |
} |
if (THIS->filename) { |
free_string(THIS->filename); |
THIS->filename = NULL; |
} |
} else { |
if (THIS->filename) { |
free_string(THIS->filename); |
THIS->filename = NULL; |
} |
copy_shared_string(THIS->filename, value->u.string); |
} |
} |
break; |
case 2: /* Function */ |
assign_svalue(&THIS->fun, value); |
break; |
default: /* Arguments */ |
assign_svalue(THIS->args->item + index - 3, value); |
break; |
} |
stack_swap(); |
pop_stack(); |
} |
|
}; |
|
/*! @endclass |
*/ |
|
/*! @endmodule |
*/ |
|
void low_backtrace(struct Pike_interpreter *i) |
{ |
struct pike_frame *f, *of = 0; |
int size = 0; |
struct array *res = NULL; |
|
for (f = i->frame_pointer; f; f = f->next) { |
size++; |
} |
|
res = allocate_array_no_init(size, 0); |
push_array(res); |
|
for (f = i->frame_pointer; f && size; f = (of = f)->next) { |
struct object *o = low_clone(backtrace_frame_program); |
struct backtrace_frame_struct *bf; |
struct identifier *function = NULL; |
|
call_c_initializers(o); |
|
size--; |
|
res->item[size].u.object = o; |
res->item[size].type = PIKE_T_OBJECT; |
res->item[size].subtype = 0; |
|
bf = OBJ2_BACKTRACE_FRAME(o); |
|
if ((bf->prog = f->context.prog)) { |
add_ref(bf->prog); |
bf->pc = f->pc + ( (f->flags & PIKE_FRAME_RETURN_INTERNAL) ? -1 : 0); |
|
} |
|
if ((bf->fun.u.object = f->current_object) && |
(bf->fun.u.object->prog)) { |
add_ref(bf->fun.u.object); |
bf->fun.subtype = f->fun; |
bf->fun.type = PIKE_T_FUNCTION; |
function = ID_FROM_INT(f->current_object->prog, f->fun); |
} else { |
bf->fun.u.integer = 0; |
bf->fun.subtype = NUMBER_DESTRUCTED; |
bf->fun.type = PIKE_T_INT; |
} |
|
if (f->locals) { |
INT32 numargs = DO_NOT_WARN((INT32) MINIMUM(f->num_args, |
i->stack_pointer - f->locals)); |
INT32 varargs = 0; |
|
if(of) |
/* f->num_args can be too large, so this is necessary for some |
* reason. I don't know why. /mast */ |
numargs = DO_NOT_WARN((INT32)MINIMUM(f->num_args,of->locals - f->locals)); |
|
numargs = MAXIMUM(numargs, 0); |
|
/* Handle varargs... */ |
if (function && (function->identifier_flags & IDENTIFIER_VARARGS) && |
(f->locals + numargs < i->stack_pointer) && |
(f->locals[numargs].type == T_ARRAY)) { |
varargs = f->locals[numargs].u.array->size; |
} |
|
if (numargs + varargs) { |
bf->args = allocate_array_no_init(numargs + varargs, 0); |
assign_svalues_no_free(bf->args->item, f->locals, numargs, BIT_MIXED); |
if (varargs) { |
assign_svalues_no_free(bf->args->item + numargs, |
f->locals[numargs].u.array->item, |
varargs, BIT_MIXED); |
} |
} |
} |
} |
/* NOTE: res has already been pushed on the stack. */ |
} |
|
/*! @decl array(Pike.BacktraceFrame) backtrace() |
*! |
*! FIXME: This documentation is not up to date! |
*! |
*! Get a description of the current call stack. |
*! |
*! The description is returned as an array with one entry for each call |
*! frame on the stack. |
*! |
*! Each entry has this format: |
*! @array |
*! @elem string file |
*! A string with the filename if known, else zero. |
*! @elem int line |
*! An integer containing the linenumber if known, else zero. |
*! @elem function fun |
*! The function that was called at this level. |
*! @elem mixed|void ... args |
*! The arguments that the function was called with. |
*! @endarray |
*! |
*! The current call frame will be last in the array. |
*! |
*! @note |
*! Please note that the frame order may be reversed in a later version |
*! (than 7.1) of Pike to accomodate for deferred backtraces. |
*! |
*! Note that the arguments reported in the backtrace are the current |
*! values of the variables, and not the ones that were at call-time. |
*! This can be used to hide sensitive information from backtraces |
*! (eg passwords). |
*! |
*! @seealso |
*! @[catch()], @[throw()] |
*/ |
PMOD_EXPORT |
PIKEFUN array(mixed) backtrace() |
efun; |
optflags OPT_EXTERNAL_DEPEND; |
{ |
low_backtrace(& Pike_interpreter); |
} |
|
#define INITIAL_BUF_LEN 4096 |
|
/*! @module String |
*/ |
|
/*! @class Buffer |
*! A buffer, used for building strings. It's |
*! conceptually similar to a string, but you can only @[add] |
*! strings to it, and you can only @[get] the value from it once. |
*! |
*! There is a reason for those seemingly rather odd limitations, |
*! it makes it possible to do some optimizations that really speed |
*! things up. |
*! |
*! You do not need to use this class unless you add very many |
*! strings together, or very large strings. |
*! |
*! @example |
*! For the fastest possible operation, write your code like this: |
*! |
*! @code{ |
*! String.Buffer b = String.Buffer( ); |
*! |
*! function add = b->add; |
*! |
*! .. call add several times in code ... |
*! |
*! string result = b->get(); // also clears the buffer |
*! @} |
*/ |
PIKECLASS Buffer |
{ |
CVAR struct string_builder str; |
CVAR int initial; |
|
void f_Buffer_get_copy( INT32 args ); |
void f_Buffer_get( INT32 args ); |
void f_Buffer_add( INT32 args ); |
|
|
/*! @decl void create(int initial_size) |
*! |
*! Initializes a new buffer. |
*! |
*! If no @[initial_size] is specified, 256 is used. If you |
*! know approximately how big the buffer will be, you can optimize |
*! the operation of @[add()] (slightly) by passing the size to this |
*! function. |
*/ |
PIKEFUN void create( int|void size ) |
{ |
struct Buffer_struct *str = THIS; |
if( args ) |
str->initial = MAXIMUM( size->u.integer, 512 ); |
else |
{ |
str->initial = 256; |
push_int(0); |
} |
} |
|
PIKEFUN string _sprintf( int flag, mapping flags ) |
{ |
switch( flag ) |
{ |
case 'O': |
{ |
struct pike_string *res; |
struct Buffer_struct *str = THIS; |
push_text( "Buffer(%d /* %d */)" ); |
if( str->str.s ) |
{ |
push_int(str->str.s->len); |
push_int(str->str.malloced); |
} |
else |
{ |
push_int( 0 ); |
push_int( 0 ); |
} |
f_sprintf( 3 ); |
res = Pike_sp[-1].u.string; |
Pike_sp--; |
RETURN res; |
} |
|
case 's': |
{ |
pop_n_elems( args ); |
if( Pike_fp->current_object->refs != 1 ) |
f_Buffer_get_copy( 0 ); |
else |
f_Buffer_get( 0 ); |
} |
return; |
|
case 't': |
RETURN make_shared_binary_string("Buffer",6); |
} |
pop_n_elems( args ); |
push_int( 0 ); |
Pike_sp[-1].subtype = 1; |
} |
|
PIKEFUN mixed cast( string type ) |
{ |
struct pike_string *string_t; |
struct pike_string *int_t; |
MAKE_CONSTANT_SHARED_STRING( string_t, "string" ); |
MAKE_CONSTANT_SHARED_STRING( int_t, "int" ); |
|
if( type == string_t ) |
{ |
pop_n_elems( args ); |
if( Pike_fp->current_object->refs != 1 ) |
f_Buffer_get_copy( 0 ); |
else |
f_Buffer_get( 0 ); |
return; |
} |
|
if( type == int_t ) |
{ |
struct Buffer_struct *str = THIS; |
pop_stack(); |
if( Pike_fp->current_object->refs != 1 ) |
f_Buffer_get_copy( 0 ); |
else |
f_Buffer_get( 0 ); |
o_cast_to_int( ); |
return; |
} |
Pike_error("Cannot cast to %s\n", type->str ); |
} |
|
PIKEFUN object `+( string what ) |
{ |
struct Buffer_struct *str = THIS, *str2; |
struct object *res = clone_object( Buffer_program, 0 ); |
|
if( str->str.s ) |
{ |
str2 = OBJ2_BUFFER( res ); |
|
if( str2->str.s ) free_string_builder( &str2->str ); |
*str2 = *str; |
init_string_builder_alloc( &str2->str, |
str->str.malloced, |
str->str.s->size_shift ); |
MEMCPY( (void *)str2->str.s, (void *)str->str.s, |
str->str.malloced+sizeof(struct pike_string)); |
} |
apply( res, "add", 1 ); |
RETURN res; |
} |
|
/*! @decl int add(string ... data) |
*! |
*! Adds @[data] to the buffer. Returns the size of the buffer. |
*! |
*/ |
PIKEFUN int add( string ... arg1 ) |
{ |
struct Buffer_struct *str = THIS; |
int j; |
|
if (!str->str.s && args) { |
int sum = 0; |
int shift = 0; |
for (j=0; j < args; j++) { |
struct pike_string *a = Pike_sp[j-args].u.string; |
sum += a->len; |
shift |= a->size_shift; |
} |
if (sum < str->initial) { |
sum = str->initial; |
} |
init_string_builder_alloc(&str->str, sum, shift & ~(shift>>1)); |
} |
|
for( j = 0; j<args; j++ ) |
{ |
struct pike_string *a = Pike_sp[j-args].u.string; |
string_builder_shared_strcat( &str->str, a ); |
} |
|
if (str->str.s) { |
RETURN str->str.s->len; |
} else { |
RETURN 0; |
} |
} |
|
/*! @decl string get_copy() |
*! |
*! Get the data from the buffer. Significantly slower than @[get], |
*! but does not clear the buffer. |
*/ |
PIKEFUN string get_copy() |
{ |
struct pike_string *str = THIS->str.s; |
if( str ) |
{ |
ptrdiff_t len = str->len; |
if( len > 0 ) |
{ |
char *d = (char *)str->str; |
switch( str->size_shift ) |
{ |
case 0: |
RETURN make_shared_binary_string(d,len); |
break; |
case 1: |
RETURN make_shared_binary_string1((short*)d,len>>1); |
break; |
case 2: |
RETURN make_shared_binary_string2((int*)d,len>>2); |
break; |
} |
} |
} |
push_text(""); |
return; |
} |
|
/*! @decl string get() |
*! |
*! Get the data from the buffer. |
*! |
*! @note |
*! This will clear the data in the buffer |
*/ |
PIKEFUN string get( ) |
{ |
struct Buffer_struct *str = THIS; |
if( str->str.s ) |
{ |
struct pike_string *s = finish_string_builder( &str->str ); |
str->str.malloced = 0; |
str->str.s = 0; |
RETURN s; |
} |
pop_n_elems(args); |
push_text(""); |
return; |
} |
|
/*! @decl int _sizeof() |
*! |
*! Returns the size of the buffer. |
*/ |
PIKEFUN int _sizeof() |
{ |
struct Buffer_struct *str = THIS; |
RETURN str->str.s ? str->str.s->len : 0; |
} |
|
INIT |
{ |
struct Buffer_struct *str = THIS; |
MEMSET( str, 0, sizeof( *str ) ); |
} |
|
EXIT |
{ |
struct Buffer_struct *str = THIS; |
if( str->str.s ) |
free_string_builder( &str->str ); |
} |
} |
|
/*! @endclass |
*/ |
|
/*! @class Replace |
*/ |
PIKECLASS multi_string_replace |
{ |
CVAR struct tupel |
{ |
int prefix; |
struct pike_string *ind; |
struct pike_string *val; |
} *v; |
CVAR size_t v_sz; |
CVAR size_t sz; |
CVAR INT32 set_start[256]; |
CVAR INT32 set_end[256]; |
|
static int replace_sortfun(struct tupel *a,struct tupel *b) |
{ |
return DO_NOT_WARN((int)my_quick_strcmp(a->ind, b->ind)); |
} |
|
PIKEFUN void create(array(string)|void from_, array(string)|void to_) |
{ |
int i; |
struct array *from; |
struct array *to; |
if (!args) { |
push_int(0); |
return; |
} |
if (!from_ || !to_) { |
Pike_error("Bad number of arguments to create().\n"); |
} |
from = from_->u.array; |
to = to_->u.array; |
if (from->size != to->size) { |
Pike_error("Replace must have equal-sized from and to arrays.\n"); |
} |
for (i = 0; i < (int)from->size; i++) { |
if (from->item[i].type != PIKE_T_STRING) { |
Pike_error("Replace: from array is not an array(string).\n"); |
} |
if (to->item[i].type != PIKE_T_STRING) { |
Pike_error("Replace: to array is not an array(string).\n"); |
} |
} |
if (THIS->v) { |
for (i = 0; i < (int)THIS->v_sz; i++) { |
if (!THIS->v[i].ind) break; |
free_string(THIS->v[i].ind); |
THIS->v[i].ind = NULL; |
free_string(THIS->v[i].val); |
THIS->v[i].val = NULL; |
} |
} |
if (THIS->v && (THIS->v_sz < (size_t)from->size)) { |
free(THIS->v); |
THIS->v = NULL; |
THIS->v_sz = 0; |
} |
if (!THIS->v) { |
THIS->v = (struct tupel *)xalloc(sizeof(struct tupel) * from->size); |
THIS->v_sz = from->size; |
} |
for (i = 0; i < (int)from->size; i++) { |
copy_shared_string(THIS->v[i].ind, from->item[i].u.string); |
copy_shared_string(THIS->v[i].val, to->item[i].u.string); |
THIS->v[i].prefix = -2; /* Uninitialized */ |
} |
THIS->sz = from->size; |
fsort((char *)THIS->v, from->size, sizeof(struct tupel), |
(fsortfun)replace_sortfun); |
|
MEMSET(THIS->set_start, 0, sizeof(INT32)*256); |
MEMSET(THIS->set_end, 0, sizeof(INT32)*256); |
|
for (i = 0; i < (int)from->size; i++) { |
INT32 x = index_shared_string(THIS->v[from->size-1-i].ind, 0); |
if ((x >= 0) && (x < 256)) |
THIS->set_start[x] = from->size-1-i; |
x = index_shared_string(THIS->v[i].ind, 0); |
if ((x >= 0) && (x < 256)) |
THIS->set_end[x] = i+1; |
} |
pop_n_elems(args); |
push_int(0); |
} |
|
static int find_longest_prefix(char *str, |
ptrdiff_t len, |
int size_shift, |
struct tupel *v, |
INT32 a, |
INT32 b) |
{ |
INT32 c,match=-1; |
ptrdiff_t tmp; |
|
while(a<b) |
{ |
c=(a+b)/2; |
|
tmp=generic_quick_binary_strcmp(v[c].ind->str, |
v[c].ind->len, |
v[c].ind->size_shift, |
str, |
MINIMUM(len,v[c].ind->len), |
size_shift); |
if(tmp<0) |
{ |
INT32 match2=find_longest_prefix(str, |
len, |
size_shift, |
v, |
c+1, |
b); |
if(match2!=-1) return match2; |
|
while(1) |
{ |
if(v[c].prefix==-2) |
{ |
v[c].prefix=find_longest_prefix(v[c].ind->str, |
v[c].ind->len, |
v[c].ind->size_shift, |
v, |
0 /* can this be optimized? */, |
c); |
} |
c=v[c].prefix; |
if(c<a || c<match) return match; |
|
if(!generic_quick_binary_strcmp(v[c].ind->str, |
v[c].ind->len, |
v[c].ind->size_shift, |
str, |
MINIMUM(len,v[c].ind->len), |
size_shift)) |
return c; |
} |
} |
else if(tmp>0) |
{ |
b=c; |
} |
else |
{ |
a=c+1; /* There might still be a better match... */ |
match=c; |
} |
} |
return match; |
} |
|
PIKEFUN string `()(string str) |
{ |
struct string_builder ret; |
ptrdiff_t length = str->len; |
ptrdiff_t s; |
int *set_start = THIS->set_start; |
int *set_end = THIS->set_end; |
struct tupel *v = THIS->v; |
int num = THIS->sz; |
|
if (!num) { |
add_ref(str); |
RETURN str; |
} |
|
init_string_builder(&ret,str->size_shift); |
|
for(s=0;length > 0;) |
{ |
INT32 a,b; |
ptrdiff_t ch; |
|
ch = index_shared_string(str, s); |
if((ch >= 0) && (ch < 256)) |
b = set_end[ch]; |
else |
b = num; |
|
if(b) |
{ |
if((ch >= 0) && (ch < 256)) |
a = set_start[ch]; |
else |
a = 0; |
|
a = find_longest_prefix(str->str+(s << str->size_shift), |
length, |
str->size_shift, |
v, a, b); |
|
if(a!=-1) |
{ |
ch = v[a].ind->len; |
if(!ch) ch=1; |
s += ch; |
length -= ch; |
string_builder_shared_strcat(&ret, v[a].val); |
continue; |
} |
} |
string_builder_putchar(&ret, |
DO_NOT_WARN((INT32)ch)); |
s++; |
length--; |
} |
|
RETURN finish_string_builder(&ret); |
} |
|
PIKEFUN array(string) _encode() |
{ |
size_t i; |
for (i=0; i < THIS->sz; i++) { |
ref_push_string(THIS->v[i].ind); |
} |
f_aggregate(DO_NOT_WARN((INT32)THIS->sz)); |
for (i=0; i < THIS->sz; i++) { |
ref_push_string(THIS->v[i].val); |
} |
f_aggregate(DO_NOT_WARN((INT32)THIS->sz)); |
f_aggregate(2); |
} |
|
PIKEFUN void _decode(array(array(string)) encoded) |
{ |
INT32 i; |
|
for (i=0; i < encoded->size; i++) { |
push_svalue(encoded->item + i); |
stack_swap(); |
} |
pop_stack(); |
|
f_multi_string_replace_create(i); |
} |
|
INIT |
{ |
THIS->v = NULL; |
THIS->v_sz = 0; |
THIS->sz = 0; |
} |
|
EXIT |
{ |
if (THIS->v) { |
int i; |
for (i = 0; i < (int)THIS->v_sz; i++) { |
if (!THIS->v[i].ind) break; |
free_string(THIS->v[i].ind); |
THIS->v[i].ind = NULL; |
free_string(THIS->v[i].val); |
THIS->v[i].val = NULL; |
} |
free(THIS->v); |
} |
THIS->v = NULL; |
THIS->v_sz = 0; |
THIS->sz = 0; |
} |
} |
|
/*! @endclass |
*/ |
|
/* NOTE: Documentation for these recides in System.pmod. */ |
PIKECLASS Time |
{ |
CVAR int hard_update; |
|
PIKEFUN int `->( string x ) |
{ |
extern struct timeval current_time; |
struct pike_string *usec; |
struct pike_string *sec; |
MAKE_CONSTANT_SHARED_STRING( sec, "sec" ); |
MAKE_CONSTANT_SHARED_STRING( usec, "usec" ); |
|
if( !x ) |
RETURN 0; |
|
if( THIS->hard_update ) |
GETTIMEOFDAY( ¤t_time ); |
|
if( x == usec ) |
RETURN current_time.tv_usec; |
if( x == sec ) |
RETURN current_time.tv_sec; |
|
#ifdef AUTO_BIGNUM |
pop_stack(); |
push_int( current_time.tv_sec ); |
push_int( 1000000 ); |
f_multiply( 2 ); |
push_int( current_time.tv_usec ); |
f_add( 2 ); |
return; |
#else |
RETURN (current_time.tv_sec * 1000000 + current_time.tv_usec); |
#endif |
} |
|
PIKEFUN int `[]( string x ) |
{ |
f_Time_cq__backtick_2D_3E( args ); |
} |
|
PIKEFUN void create( int|void fast ) |
{ |
THIS->hard_update = !fast; |
} |
} |
|
PIKECLASS Timer |
{ |
CVAR struct timeval last_time; |
CVAR int hard_update; |
|
|
PIKEFUN float peek( ) |
{ |
extern struct timeval current_time; |
FLOAT_TYPE res; |
if( THIS->hard_update ) |
GETTIMEOFDAY( ¤t_time ); |
res = current_time.tv_sec-THIS->last_time.tv_sec + |
(current_time.tv_usec-THIS->last_time.tv_usec)/1000000.0; |
RETURN res; |
} |
|
PIKEFUN float get( ) |
{ |
extern struct timeval current_time; |
f_Timer_peek( 0 ); |
THIS->last_time = current_time; |
return; |
} |
|
PIKEFUN void create( int|void fast ) |
{ |
extern struct timeval current_time; |
THIS->hard_update = !fast; |
if( THIS->hard_update ) |
GETTIMEOFDAY( ¤t_time ); |
THIS->last_time = current_time; |
} |
} |
|
|
/*! @class SingleReplace |
*/ |
PIKECLASS single_string_replace |
{ |
CVAR SearchMojt mojt; |
CVAR struct pike_string *del; |
CVAR struct pike_string *to; |
|
INIT |
{ |
THIS->mojt.vtab = NULL; |
THIS->mojt.data = NULL; |
THIS->del = NULL; |
THIS->to = NULL; |
} |
|
EXIT |
{ |
if (THIS->mojt.vtab) { |
THIS->mojt.vtab->freeme(THIS->mojt.data); |
THIS->mojt.vtab = NULL; |
THIS->mojt.data = NULL; |
} |
if (THIS->del) { |
free_string(THIS->del); |
THIS->del = NULL; |
} |
if (THIS->to) { |
free_string(THIS->to); |
THIS->to = NULL; |
} |
} |
|
PIKEFUN void create(string|void del_, string|void to_) |
{ |
struct pike_string *del; |
struct pike_string *to; |
|
/* Clean up... */ |
exit_single_string_replace_struct(); |
|
if (!del_) return; |
|
if (!to_) { |
SIMPLE_BAD_ARG_ERROR("String.SingleReplace->create", 2, "string"); |
} |
|
if (del_->u.string == to_->u.string) { |
/* No-op... */ |
return; |
} |
|
copy_shared_string(THIS->del, del = del_->u.string); |
copy_shared_string(THIS->to, to = to_->u.string); |
|
if (del->len) { |
THIS->mojt = simple_compile_memsearcher(del); |
} |
} |
|
/*** replace function ***/ |
typedef char *(* replace_searchfunc)(void *,void *,size_t); |
PIKEFUN string `()(string str) |
{ |
int shift; |
struct pike_string *del = THIS->del; |
struct pike_string *to = THIS->to; |
struct pike_string *ret = NULL; |
|
if (!str->len || !del || !to) { |
/* The result is already on the stack in the correct place... */ |
return; |
} |
|
shift = MAXIMUM(str->size_shift, to->size_shift); |
|
if (!del->len) { |
int e, pos; |
ret = begin_wide_shared_string(str->len + to->len * (str->len-1), |
shift); |
low_set_index(ret, 0, index_shared_string(str, 0)); |
for(pos=e=1;e<str->len;e++) |
{ |
pike_string_cpy(MKPCHARP_STR_OFF(ret,pos), to); |
pos+=to->len; |
low_set_index(ret,pos++,index_shared_string(str,e)); |
} |
} else { |
char *s, *end, *tmp; |
replace_searchfunc f = (replace_searchfunc)0; |
void *mojt_data = THIS->mojt.data; |
PCHARP r; |
|
end = str->str+(str->len<<str->size_shift); |
|
switch(str->size_shift) |
{ |
case 0: f = (replace_searchfunc)THIS->mojt.vtab->func0; break; |
case 1: f = (replace_searchfunc)THIS->mojt.vtab->func1; break; |
case 2: f = (replace_searchfunc)THIS->mojt.vtab->func2; break; |
#ifdef PIKE_DEBUG |
default: fatal("Illegal shift.\n"); |
#endif |
} |
|
if(del->len == to->len) |
{ |
ret = begin_wide_shared_string(str->len, shift); |
} else { |
INT32 delimiters = 0; |
|
s = str->str; |
|
while((s = f(mojt_data, s, (end-s)>>str->size_shift))) |
{ |
delimiters++; |
s += del->len << str->size_shift; |
} |
|
if (!delimiters) { |
/* The result is already on the stack in the correct place... */ |
return; |
} |
|
ret = begin_wide_shared_string(str->len + |
(to->len-del->len)*delimiters, shift); |
} |
|
s = str->str; |
r = MKPCHARP_STR(ret); |
|
while((tmp = f(mojt_data, s, (end-s)>>str->size_shift))) |
{ |
#ifdef PIKE_DEBUG |
if(tmp + (del->len << str->size_shift) > end) |
fatal("generic_memory_search found a match beyond end of string!\n"); |
#endif |
generic_memcpy(r,MKPCHARP(s,str->size_shift),(tmp-s)>>str->size_shift); |
INC_PCHARP(r,(tmp-s)>>str->size_shift); |
pike_string_cpy(r,to); |
INC_PCHARP(r,to->len); |
s=tmp+(del->len << str->size_shift); |
} |
generic_memcpy(r,MKPCHARP(s,str->size_shift),(end-s)>>str->size_shift); |
} |
RETURN end_shared_string(ret); |
} |
|
PIKEFUN array(string) _encode() |
{ |
if (THIS->del) { |
ref_push_string(THIS->del); |
ref_push_string(THIS->to); |
f_aggregate(2); |
} else { |
push_int(0); |
} |
} |
|
PIKEFUN void _decode(array(string)|int(0..0) encoded_) |
{ |
INT32 i = 0; |
if (encoded_->type == PIKE_T_ARRAY) { |
struct array *encoded = encoded_->u.array; |
|
for (i=0; i < encoded->size; i++) { |
push_svalue(encoded->item + i); |
stack_swap(); |
} |
} |
pop_stack(); |
|
f_single_string_replace_create(i); |
} |
} |
|
/*! @endclass |
*/ |
|
/*! @endmodule |
*/ |
|
|
PIKECLASS automap_marker |
{ |
PIKEVAR array arg; |
PIKEVAR int depth; |
|
PIKEFUN void create(array a, int d) |
{ |
if(THIS->arg) free_array(THIS->arg); |
add_ref(THIS->arg=a); |
THIS->depth=d; |
} |
|
PIKEFUN string _sprintf(int mode, mapping flags) |
{ |
pop_n_elems(args); |
push_text("%O%*'[*]'n"); |
if(THIS->arg) |
ref_push_array(THIS->arg); |
else |
push_int(0); |
push_int(THIS->depth*3); |
f_sprintf(3); |
} |
} |
|
|
static void low_automap(int d, |
int depth, |
struct svalue *fun, |
struct svalue *real_args, |
INT32 args) |
{ |
INT32 x,e,tmp,size=0x7fffffff; |
struct svalue *tmpargs=Pike_sp - args; |
struct array *ret; |
|
for(e=0;e<args;e++) |
{ |
if(real_args[e].type==T_OBJECT && |
real_args[e].u.object->prog == automap_marker_program && |
OBJ2_AUTOMAP_MARKER(real_args[e].u.object)->depth >= d) |
{ |
if(tmpargs[e].type != T_ARRAY) |
index_error("__automap__", |
Pike_sp-args, |
args, |
tmpargs, |
NULL, |
"Automap on non-array.\n"); |
tmp=tmpargs[e].u.array->size; |
if(tmp < size) |
size=tmp; |
} |
} |
|
#ifdef PIKE_DEBUG |
if(size == 0x7fffffff) |
fatal("No automap markers found in low_automap\n"); |
#endif |
|
push_array(ret=allocate_array(size)); |
|
for(x=0;x<size;x++) |
{ |
for(e=0;e<args;e++) |
{ |
if(real_args[e].type==T_OBJECT && |
real_args[e].u.object->prog == automap_marker_program && |
OBJ2_AUTOMAP_MARKER(real_args[e].u.object)->depth >= d) |
{ |
#ifdef PIKE_DEBUG |
if(x >= tmpargs[e].u.array->size) |
fatal("low_automap failed to determine size!\n"); |
#endif |
push_svalue(ITEM(tmpargs[e].u.array)+x); |
}else{ |
push_svalue(tmpargs+e); |
} |
} |
|
if(d == depth) |
apply_svalue(fun,args); |
else |
low_automap(d+1,depth,fun,real_args,args); |
ITEM(ret)[x]=*--Pike_sp; |
} |
stack_unlink(args); |
} |
|
|
PIKEFUN array __automap__(mixed fun, mixed ... tmpargs) |
efun; |
{ |
int e,depth=-1; |
check_stack(args); |
|
for(e=0;e<args-1;e++) |
{ |
if(tmpargs[e].type==T_OBJECT && |
tmpargs[e].u.object->prog == automap_marker_program) |
{ |
int tmp=OBJ2_AUTOMAP_MARKER(tmpargs[e].u.object)->depth; |
if(tmp > depth) depth=tmp; |
ref_push_array(OBJ2_AUTOMAP_MARKER(tmpargs[e].u.object)->arg); |
}else{ |
push_svalue(tmpargs+e); |
} |
} |
check_stack(depth * (args+1)); |
low_automap(1,depth,fun,tmpargs,args-1); |
stack_unlink(args); |
} |
|
void init_builtin(void) |
{ |
INIT |
} |
|
void exit_builtin(void) |
{ |
EXIT |
} |
|