#include <varargs.h> #include <stdio.h> #include <setjmp.h> #include <string.h> #include <ctype.h> #include <sys/time.h> #include <sys/types.h> /* sys/types.h and netinet/in.h are here to enable include of comm.h below */ #include <sys/stat.h> /* #include <netinet/in.h> Included in comm.h below */ #include <memory.h> #include <math.h> #include "config.h" #include "lint.h" #include "lang.h" #include "exec.h" #include "interpret.h" #include "object.h" #include "instrs.h" #include "patchlevel.h" #include "comm.h" #include "switch.h" #include "mapping.h" #include "mudstat.h" #include "lex.h" #ifdef RUSAGE /* Defined in config.h */ #ifndef SOLARIS #include <sys/resource.h> extern int getrusage (int, struct rusage *); #ifdef sun extern int getpagesize(); #endif #ifndef RUSAGE_SELF #define RUSAGE_SELF 0 #endif #else /* SOLARIS */ #include <sys/times.h> #include <limits.h> #endif /* SOLARIS */ #endif struct fkntab { char *name; unsigned short inherit_index; unsigned short function_index; }; extern struct object *master_ob; extern void print_svalue (struct svalue *, struct object *); struct svalue *sapply (char *, struct object *, int, int); static void do_trace (char *, char *, char *); static int apply_low (char *, struct object *, int, int); static int strpref (char *, char *); extern int do_rename (char *, char *); static int inter_sscanf (int); extern struct object *previous_ob; extern char *last_verb; extern struct svalue const0, const1; struct program *current_prog; extern int current_time, s_flag; extern struct object *current_heart_beat, *current_interactive; int variable_index_found; int variable_inherit_found; int variable_type_mod_found; int cache_tries = 0, cache_hits = 0; #ifdef CACHE_STATS int call_cache_saves = 0; int global_cache_saves = 0; int searches_needed = 0; int searches_done = 0; int global_first_saves = 0; int call_first_saves = 0; #endif static int tracedepth; #define TRACE_CALL 1 #define TRACE_CALL_OTHER 2 #define TRACE_RETURN 4 #define TRACE_ARGS 8 #define TRACE_EXEC 16 #define TRACE_HEART_BEAT 32 #define TRACE_APPLY 64 #define TRACE_OBJNAME 128 #define TRACETST(b) (command_giver->interactive->trace_level & (b)) #define TRACEP(b) \ (command_giver && command_giver->interactive && TRACETST(b) && \ (command_giver->interactive->trace_prefix == 0 || \ (current_object && strpref(command_giver->interactive->trace_prefix, \ current_object->name))) ) #define TRACEHB (current_heart_beat == 0 || (command_giver->interactive->trace_level \ & TRACE_HEART_BEAT)) /* * Inheritance: * An object X can inherit from another object Y. This is done with * the statement inherit "file"; * The inherit statement will clone a copy of that file, call reset * in it, and set a pointer to Y from X. * Y has to be removed from the linked list of all objects. * All variables declared by Y will be copied to X, so that X has access * to them. * * If Y isnt loaded when it is needed, X will be discarded, and Y will be * loaded separetly. X will then be reloaded again. */ /* */ extern int d_flag; extern int current_line, eval_cost; /* * These are the registers used at runtime. * The control stack saves registers to be restored when a function * will return. That means that control_stack[0] will have almost no * interesting values, as it will terminate execution. */ static char *pc; /* Program pointer. */ static struct svalue *fp; /* Pointer to first argument. */ struct svalue *sp; /* Points to value of last push. */ int inh_offset; /* Needed for inheritance */ struct svalue start_of_stack[EVALUATOR_STACK_SIZE]; struct svalue catch_value; /* Used to throw an error to a catch */ static struct control_stack control_stack[MAX_TRACE]; struct control_stack *csp; /* Points to last element pushed */ #ifdef COUNT_CALLS /* Temporary */ static int num_call_self, num_call_down, num_call_other; #endif /* These are set by search_for_function if it is successful * function_inherit_found == num_inherit if in top program * function_prog_found == Implied by inherit_found */ int function_inherit_found = -1; struct program *function_prog_found = 0; int function_index_found = -1; unsigned short function_type_mod_found = 0; /* * Information about assignments of values: * * There are three types of l-values: Local variables, global variables * and vector elements. * * The local variables are allocated on the stack together with the arguments. * the register 'frame_pointer' points to the first argument. * * The global variables must keep their values between executions, and * have space allocated at the creation of the object. * * Elements in vectors are similar to global variables. There is a reference * count to the whole vector, that states when to deallocate the vector. * The elements consists of 'struct svalue's, and will thus have to be freed * immediately when over written. */ /* * Push an object pointer on the stack. Note that the reference count is * incremented. * A destructed object must never be pushed onto the stack. */ INLINE void push_object(struct object *ob) { sp++; if (sp == &start_of_stack[EVALUATOR_STACK_SIZE]) fatal("stack overflow\n"); if (ob) { sp->type = T_OBJECT; sp->u.ob = ob; add_ref(ob, "push_object"); } else { sp->type = T_NUMBER; sp->u.number = 0; } } /* * Push a number on the value stack. */ INLINE void push_number(int n) { sp++; if (sp == &start_of_stack[EVALUATOR_STACK_SIZE]) fatal("stack overflow\n"); sp->type = T_NUMBER; sp->u.number = n; } INLINE void push_float(float f) { sp++; if (sp == &start_of_stack[EVALUATOR_STACK_SIZE]) fatal("stack overflow\n"); sp->type = T_FLOAT; sp->u.real = f; } /* * Push a string on the value stack. */ INLINE void push_string(char *p, int type) { sp++; if (sp == &start_of_stack[EVALUATOR_STACK_SIZE]) fatal("stack overflow\n"); sp->type = T_STRING; sp->string_type = type; switch(type) { case STRING_MALLOC: sp->u.string = string_copy(p); break; case STRING_SHARED: sp->u.string = make_shared_string(p); break; case STRING_CONSTANT: sp->u.string = p; break; } } /* * Get address to a valid global variable. */ static INLINE struct svalue * find_value(int inh, int rnum) { int num; if ((inh == 255 && rnum == 255)) { error("Referencing undefined variable (inh == %d, var_num == %d, variables are %s).\n", inh, rnum, current_object->variables?"defined":"undefined"); } if (inh == 255) inh = 0; else inh -= current_prog->num_inherited - 1; #ifdef DEBUG if (inh > 0) fatal("Illegal variable access, %d(off %d). See trace above.\n", inh, current_prog->num_inherited); if (rnum > current_object->prog->inherit[inh_offset + inh].prog->num_variables - 1) fatal("Illegal variable access, variable %d(off %d, in %d). See trace above.\n", rnum, current_object->prog->inherit[inh_offset + inh].prog->num_variables, inh_offset + inh); #endif num = current_object->prog-> inherit[inh_offset + inh].variable_index_offset + rnum; return ¤t_object->variables[num]; } /* * Free the data that an svalue is pointing to. Not the svalue * itself. */ void free_svalue(struct svalue *v) { switch(v->type) { case T_STRING: switch(v->string_type) { case STRING_MALLOC: free(v->u.string); break; case STRING_SHARED: free_string(v->u.string); break; } break; case T_OBJECT: free_object(v->u.ob, "free_svalue"); break; case T_POINTER: free_vector(v->u.vec); break; case T_MAPPING: free_mapping(v->u.map); break; case T_INVALID: fatal("Invalid value of variable!\n"); break; } *v = const0; /* marion - clear this value all away */ } /* * Prepend a slash in front of a string. */ char * add_slash(char *str) { char *tmp; tmp = xalloc(strlen(str)+2); strcpy(tmp,"/"); strcat(tmp, str); return tmp; } /* * Assign to a svalue. * This is done either when element in vector, or when to an identifier * (as all identifiers are kept in a vector pointed to by the object). */ INLINE void assign_svalue_no_free(struct svalue *to, struct svalue *from) { #ifdef DEBUG if (from == 0) fatal("Null pointer to assign_svalue().\n"); #endif *to = *from; switch(from->type) { case T_STRING: switch(from->string_type) { case STRING_MALLOC: /* No idea to make the string shared */ #ifdef ALWAYS_SHARE to->string_type = STRING_SHARED; to->u.string = make_shared_string(from->u.string); #else to->u.string = string_copy(from->u.string); #endif break; case STRING_CONSTANT: break; case STRING_SHARED: /* It already is shared */ increment_string_ref(from->u.string); break; default: fatal("Bad string type %d\n", from->string_type); } break; case T_OBJECT: add_ref(to->u.ob, "ass to var"); break; case T_POINTER: to->u.vec->ref++; break; case T_MAPPING: to->u.map->ref++; break; } } INLINE void assign_svalue(struct svalue *dest, struct svalue *v) { /* First deallocate the previous value. */ free_svalue(dest); assign_svalue_no_free(dest, v); } #if 0 /* This function has been replaced with a macro to save speed. Macro is defined * in interpret.h */ void push_svalue(struct svalue *v) { sp++; assign_svalue_no_free(sp, v); } #endif /* * Pop the top-most value of the stack. * Don't do this if it is a value that will be used afterwards, as the * data may be sent to free(), and destroyed. */ INLINE void pop_stack() { #ifdef DEBUG if (sp < start_of_stack) fatal("Stack underflow.\n"); #endif free_svalue(sp); sp--; } /* * Compute the address of an array element. */ INLINE static void push_indexed_lvalue(int needlval) { struct svalue *i, *vec, *item; int ind; i = sp; vec = sp - 1; if (vec->type != T_MAPPING) { if (i->type != T_NUMBER || i->u.number < 0) error("Illegal index\n"); ind = i->u.number; } switch(vec->type) { case T_STRING: { static struct svalue one_character; /* marion says: this is a crude part of code */ pop_stack(); one_character.type = T_NUMBER; if (ind > strlen(vec->u.string) || ind < 0) one_character.u.number = 0; else one_character.u.number = vec->u.string[ind]; free_svalue(sp); sp->type = T_LVALUE; sp->u.lvalue = &one_character; break;} case T_POINTER: pop_stack(); if (ind >= vec->u.vec->size) error ("Index out of bounds\n"); item = &vec->u.vec->item[ind]; if (vec->u.vec->ref == 1) { static struct svalue quickfix = { T_NUMBER }; /* marion says: but this is crude too */ /* marion blushes. */ assign_svalue (&quickfix, item); item = &quickfix; } free_svalue(sp); /* This will make 'vec' invalid to use */ sp->type = T_LVALUE; sp->u.lvalue = item; break; case T_MAPPING: item = get_map_lvalue(vec->u.map, i, needlval); pop_stack(); if (vec->u.map->ref == 1) { static struct svalue quickfix = { T_NUMBER }; assign_svalue (&quickfix, item); item = &quickfix; } free_svalue(sp); /* This will make 'vec' invalid to use */ sp->type = T_LVALUE; sp->u.lvalue = item; break; default: error("Indexing on illegal type.\n"); break; } } #ifdef OPCPROF #define MAXOPC 512 static int opcount[MAXOPC]; #endif /* * Deallocate 'n' values from the stack. */ INLINE void pop_n_elems(int n) { #ifdef DEBUG if (n < 0) fatal("pop_n_elems: %d elements.\n", n); #endif for (; n > 0; n--) pop_stack(); } void bad_arg(int arg, int instr, struct svalue *sv) { char *type_name = "Unknown"; switch(sv->type) { case T_NUMBER: type_name = "Integer"; break; case T_STRING: type_name = "String"; break; case T_POINTER: type_name = "Array"; break; case T_OBJECT: type_name = "Object"; break; case T_MAPPING: type_name = "Mapping"; break; case T_FLOAT: type_name = "Float"; break; } error("Bad argument %d to %s(), received type was %s \n", arg, get_f_name(instr), type_name); } /* * Time spent in specific function */ void time_funs(struct function *to, struct function *from) { #ifdef RUSAGE #ifdef SOLARIS struct tms buffer; clock_t ticks; if (times(&buffer) != -1) { ticks = buffer.tms_utime + buffer.tms_stime; if (from) from->time_spent += (ticks - from->ticks_call); if (to) to->ticks_call = ticks; } #else struct rusage rus; int cpu_s, cpu_us; if (getrusage(RUSAGE_SELF, &rus) >= 0) { cpu_s = rus.ru_utime.tv_sec + rus.ru_stime.tv_sec; cpu_us = rus.ru_utime.tv_usec + rus.ru_stime.tv_usec; } if (from) { from->time_spent += (cpu_s - from->stime_call) * 100000 + (cpu_us - from->utime_call) / 10; } if (to) { to->stime_call = cpu_s; to->utime_call = cpu_us; } #endif #endif } void save_control_context(struct control_stack *csp) { csp->ob = current_object; csp->prev_ob = previous_ob; csp->fp = fp; csp->funp = 0; csp->prog = current_prog; /* csp->extern_call = 0; It is set by eval_instruction() */ if (current_prog) csp->pc = pc - current_prog->program; csp->inh_offset = inh_offset; } void push_control_stack(struct function *funp) { if (csp == &control_stack[MAX_TRACE-1]) error("Too deep recursion.\n"); csp++; save_control_context(csp); csp->funp = funp; /* Only used for tracebacks */ #ifdef PROFILE_FUNS if (funp) funp->num_calls++; time_funs(csp->funp, (csp == control_stack) ? 0 : (csp-1)->funp); #endif } /* * Pop the control stack one element, and restore registers. * extern_call must not be modified here, as it is used imediately after pop. */ void restore_control_context(struct control_stack *csp) { current_object = csp->ob; if (current_object) access_object(csp->ob); current_prog = csp->prog; if(current_prog) { access_program(csp->prog); pc = current_prog->program + csp->pc; } previous_ob = csp->prev_ob; fp = csp->fp; inh_offset = csp->inh_offset; } void pop_control_stack() { #ifdef DEBUG if (csp == control_stack - 1) fatal("Popped out of the control stack"); #endif restore_control_context(csp); #ifdef PROFILE_FUNS time_funs(csp == control_stack ? 0 : (csp-1)->funp, csp->funp); #endif csp--; } /* * Push a pointer to a vector on the stack. Note that the reference count * is incremented. Newly created vectors normally have a reference count * initialized to 1. */ INLINE void push_vector(struct vector *v) { v->ref++; sp++; sp->type = T_POINTER; sp->u.vec = v; } INLINE void push_mapping(struct mapping *v) { v->ref++; sp++; sp->type = T_MAPPING; sp->u.map = v; } /* * Push a string on the stack that is already malloced. */ void INLINE push_malloced_string(char *p) { sp++; sp->type = T_STRING; sp->u.string = p; sp->string_type = STRING_MALLOC; } /* * Push a string on the stack that is already constant. */ INLINE void push_constant_string(char *p) { sp++; sp->type = T_STRING; sp->u.string = p; sp->string_type = STRING_CONSTANT; } extern char *string_print_formatted (int , char *, int, struct svalue *); static void do_trace_call(struct function *funp) { do_trace("Call direct ", funp->name, " "); if (TRACEHB) { if (TRACETST(TRACE_ARGS)) { int i; char buff[1024]; sprintf(buff, " with %d arguments: ", funp->num_arg); write_socket(buff, command_giver); for(i = 0; i < funp->num_arg; i++) write_socket(string_print_formatted(0, "%O ", 1, &fp[i]), command_giver); } write_socket("\n", command_giver); } } static unsigned int previous_ob_access_time; /* * Argument is the function to execute. * The function is located in current_prog. * There is a number of arguments on the stack. Normalize them and initialize * local variables, so that the called function is pleased. */ char * setup_new_frame(struct function *funp) { int called_args; unsigned short int npc; char *off; /* Remove excessive arguments, or put them in argv if applicable */ previous_ob_access_time = current_object->time_of_ref; access_program(current_prog); access_object(current_object); if (funp->type_flags & TYPE_MOD_TRUE_VARARGS) { if (csp->num_local_variables >= funp->num_arg) { struct vector *v; int i, narg; v = allocate_array(narg = csp->num_local_variables - (funp->num_arg - 1)); for (i = narg - 1; i >= 0; i--) assign_svalue_no_free(&v->item[narg - 1 - i], &sp[-i]); pop_n_elems(narg); push_vector(v); v->ref--; /* to make reference 1 again */ csp->num_local_variables -= narg - 1; } } else while(csp->num_local_variables > funp->num_arg) { pop_stack(); csp->num_local_variables--; } /* Correct number of arguments and local variables */ called_args = csp->num_local_variables; while(csp->num_local_variables < funp->num_arg + (int)funp->num_local) { push_number(0); csp->num_local_variables++; } #ifdef DEBUG if (called_args > funp->num_arg) fatal("Error in seting up call frame!\n"); #endif if (called_args == funp->num_arg) npc = funp->offset + funp->num_arg * 2; else { off = current_prog->program + funp->offset + called_args * 2; ((char *)&npc)[0] = off[0]; ((char *)&npc)[1] = off[1]; } tracedepth++; fp = sp - csp->num_local_variables + 1; #ifdef TRACE_CODE if (TRACEP(TRACE_CALL)) { do_trace_call(funp); } #endif return current_prog->program + npc; } static void break_point() { if (sp - fp - csp->num_local_variables + 1 != 0) fatal("Bad stack pointer.\n"); } /* marion * maintain a small and inefficient stack of error recovery context * data structures. * This routine is called in three different ways: * push=-1 Pop the stack. * push=1 push the stack. * push=0 No error occured, so the pushed value does not have to be * restored. The pushed value can simply be popped into the void. * * The stack is implemented as a linked list of stack-objects, allocated * from the heap, and deallocated when popped. */ void push_pop_error_context (int push) { extern jmp_buf error_recovery_context; extern int error_recovery_context_exists; static struct error_context_stack { jmp_buf old_error_context; int old_exists_flag; struct control_stack *save_csp; struct object *save_command_giver; struct svalue *save_sp; struct error_context_stack *next; struct control_stack cstack; } *ecsp = 0, *p; if (push == 1) { /* * Save some global variables that must be restored separately * after a longjmp. The stack will have to be manually popped all * the way. */ p = (struct error_context_stack *)xalloc (sizeof *p); save_control_context(&(p->cstack)); p->save_sp = sp; p->save_csp = csp; p->save_command_giver = command_giver; memcpy ( (char *)p->old_error_context, (char *)error_recovery_context, sizeof error_recovery_context); p->old_exists_flag = error_recovery_context_exists; p->next = ecsp; ecsp = p; } else { p = ecsp; if (p == 0) fatal("Catch: error context stack underflow"); if (push == 0) { #ifdef DEBUG if (csp != p->save_csp-1) fatal("Catch: Lost track of csp"); #if 0 /* * This test is not valid! The statement catch(exec("...")) will * change the value of command_giver. */ if (command_giver != p->save_command_giver) fatal("Catch: Lost track of command_giver"); #endif #endif } else { /* push == -1 ! * They did a throw() or error. That means that the control * stack must be restored manually here. */ csp = p->save_csp; pop_n_elems (sp - p->save_sp); command_giver = p->save_command_giver; } memcpy ((char *)error_recovery_context, (char *)p->old_error_context, sizeof error_recovery_context); error_recovery_context_exists = p->old_exists_flag; ecsp = p->next; restore_control_context(&(p->cstack)); free ((char *)p); } } /* * May current_object shadow object ob ? We rely heavily on the fact that * function names are pointers to shared strings, which means that equality * can be tested simply through pointer comparison. */ int validate_shadowing(struct object *ob) { int i, j; struct program *shadow = current_object->prog, *victim = ob->prog; struct svalue *ret; if (current_object->shadowing) error("shadow: Already shadowing.\n"); if (current_object->shadowed) error("shadow: Can't shadow when shadowed.\n"); if (current_object->super) error("The shadow must not reside inside another object.\n"); if (ob->shadowing) error("Can't shadow a shadow.\n"); /* Loop structure copied from search_for_function... *shrug* /Dark */ { int inh; for (inh = ob->prog->num_inherited - 1; inh >= 0;--inh) { int fun; struct program *progp = victim->inherit[inh].prog; access_program(progp); for (fun = progp->num_functions - 1; fun >= 0; fun--) { /* Should static functions 'shadowing' nomask functions be allowed? They do not do any harm... */ if ( (progp->functions[fun].type_flags & TYPE_MOD_NO_MASK) && search_for_function(progp->functions[fun].name, shadow) ) error("Illegal to shadow 'nomask' function \"%s\".\n", progp->functions[fun].name); } } } if (current_object == master_ob) return 1; push_object(ob); ret = apply_master_ob(M_QUERY_ALLOW_SHADOW, 1); if (!(ob->flags & O_DESTRUCTED) && ret && !(ret->type == T_NUMBER && ret->u.number == 0)) { return 1; } return 0; } /* * 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 wont crash when doing simple operations like assign_svalue * on a destructed object. You have to watch out, of course, that you dont * apply a function to it. * to save space it is preferable that destructed objects are freed soon. * amylaar */ void check_for_destr(struct svalue *arg) { int i, change; struct vector *v; struct mapping *m; struct apair *p, **pp; switch (arg->type) { case T_POINTER: v = arg->u.vec; for (i = 0; i < v->size; i++) { if (v->item[i].type != T_OBJECT) continue; if (!(v->item[i].u.ob->flags & O_DESTRUCTED)) continue; assign_svalue(&v->item[i], &const0); } break; case T_MAPPING: m = arg->u.map; /* Value parts that have been destructed are kept but set = 0. */ for (i = 0 ; i < m->size ; i++) { for (p = m->pairs[i]; p ; p = p->next) { if (p->val.type == T_OBJECT && p->val.u.ob->flags & O_DESTRUCTED) assign_svalue(&p->val, &const0); } } /* Index parts that has been destructed is removed */ change = 1; do { for (i = 0 ; i < m->size ; i++) { for (pp = &m->pairs[i]; *pp; ) { p = *pp; if (p->arg.type == T_OBJECT && p->arg.u.ob->flags & O_DESTRUCTED) { *pp = p->next; free_svalue(&p->arg); free_svalue(&p->val); free((char *)p); m->card--; } else { pp = &p->next; } } } change = 0; } while (change != 0); break; default: error("Strange type to check_for_destr.\n"); break; } } /* * Evaluate instructions at address 'p'. All program offsets are * to current_prog->program. 'current_prog' must be setup before * call of this function. * * There must not be destructed objects on the stack. The destruct_object() * function will automatically remove all occurences. The effect is that * all called efuns knows that they wont have destructed objects as * arguments. */ #ifdef TRACE_CODE int previous_instruction[60]; int stack_size[60]; char *previous_pc[60]; static int last; #endif static int num_arg; extern char *string_print_formatted (int , char *, int, struct svalue *); extern char *break_string (char *, int, struct svalue *); extern struct mapping *copy_mapping(struct mapping *); extern struct mapping *filter_map (struct mapping *, char *, struct object *, struct svalue *); extern char *query_ip_number (struct object *); extern char *query_ip_name (struct object *); extern struct vector *get_local_commands (struct object *); extern struct vector *subtract_array (struct vector *,struct vector*); extern struct vector *intersect_array (struct vector *, struct vector *); extern char *string_print_formatted (int, char *, int, struct svalue *); extern struct svalue *debug_command (char *, int, struct svalue *); extern struct vector *subtract_array (struct vector*,struct vector*); extern struct vector *intersect_array (struct vector*,struct vector*); extern struct vector *make_unique (struct vector *arr,char *func, struct svalue *skipnum); extern struct mapping *map_map (struct mapping *, char *, struct object *, struct svalue *); static void eval_instruction(char *); #ifdef RUSAGE #define MAX_CPU_STACK 1000 static int progcpui; static struct progcpuS { /* no good idea to do this with a list */ struct program *prog; /* an array is much faster... */ long cpu; } progcpu[MAX_CPU_STACK]; void clear_cpu_stack() { progcpui = 1; progcpu[0].prog = 0; progcpu[0].cpu = (-1); } void remove_cpu_stack(struct program *prog) { int i; for(i = progcpui; i >= 0; i--) if (progcpu[i].prog = prog) progcpu[i].prog = (struct program *)0; } #endif static void f_last_reference_time(int num_arg) { push_number(previous_ob_access_time); } static void f_assertion(int num_arg) { short offset; short assertion_type; ((char *)&assertion_type)[0] = *pc++; ((char *)&assertion_type)[1] = *pc++; ((char *)&offset)[0] = *pc++; ((char *)&offset)[1] = *pc++; if ((current_prog->debug_flags | current_object->debug_flags) & assertion_type) return; else pc = current_prog->program + offset; } static void f_fail_precond(int num_arg) { int val; val = sp->u.number; pop_stack(); if (!val) error("Precondition failed!"); } static void f_fail_postcond(int num_arg) { int val; val = sp->u.number; pop_stack(); if (!val) error("Postcondition failed!"); } static void f_fail_invariant(int num_arg) { int val; val = sp->u.number; pop_stack(); if (!val) error("Invariant failed!"); } static void f_fail_assertion(int num_arg) { int val; val = sp->u.number; pop_stack(); if (!val) error("Assertion failed!"); } static void f_do_precond(int num_arg) { unsigned short offset; if ((current_prog->debug_flags | current_object->debug_flags) & 0x100) { ((char *)&offset)[0] = pc[0]; ((char *)&offset)[1] = pc[1]; pc = current_prog->program + offset; } else pc += 2; } static void f_do_postcond(int num_arg) { unsigned short offset; if ((current_prog->debug_flags | current_object->debug_flags) & 0x200) { ((char *)&offset)[0] = pc[0]; ((char *)&offset)[1] = pc[1]; pc = current_prog->program + offset; } else pc += 2; } static void f_do_invariants(int num_arg) { int i; for (i = current_prog->num_inherited - 1; i >= 0; i--) { if ((current_prog->inherit[i].prog->debug_flags | current_object->debug_flags) & 0x400 && current_prog->inherit[i].prog->invariant != (unsigned short) -1 && !(current_prog->inherit[i].type & TYPE_MOD_SECOND)) { call_function(current_object, inh_offset + i - (current_prog->num_inherited - 1), current_prog->inherit[i].prog->invariant, 0); pop_stack(); } } } static void f_larrow(int num_arg) { fatal("f_larrow should not be called.\n"); } static void f_darrow(int num_arg) { fatal("f_darrow should not be called.\n"); } static void f_ext(int num_arg) { fatal("f_ext should not be called.\n"); } static void f_call_virt(int num_arg) { unsigned short func_name_index, fix, fiix; struct function *funp; int num_args; char *func; #ifdef COUNT_CALLS num_call_self++; #endif cache_tries++; fiix = EXTRACT_UCHAR(pc); pc++; ((char *)&fix)[0] = pc[0]; ((char *)&fix)[1] = pc[1]; pc += 2; num_args = EXTRACT_UCHAR(pc); pc++; if (current_object->prog == current_prog) { cache_hits++; #ifdef CACHE_STATS call_first_saves += current_prog->num_inherited - fiix; #endif function_prog_found = current_object->prog-> inherit[fiix].prog; function_inherit_found = fiix; function_index_found = fix; } else { access_program(current_prog->inherit[fiix].prog); func = current_prog->inherit[fiix].prog->functions[fix].name; s_f_f(func, current_object->prog, current_prog, fiix, fix); if (function_type_mod_found & TYPE_MOD_PRIVATE && inh_offset < function_inherit_found - function_prog_found->num_inherited + 1) error("Atempted call of private function.\n"); } access_program(function_prog_found); funp = &(function_prog_found->functions[function_index_found]); /* Urgle. There should probably be a function for all this. |D| * See call-self for comments */ push_control_stack (funp); inh_offset = function_inherit_found; current_prog = function_prog_found; csp->ext_call = 0; csp->num_local_variables = num_args; pc = setup_new_frame(funp); csp->extern_call = 0; #ifdef RUSAGE { #ifdef SOLARIS struct tms buffer; #else struct rusage rus; #endif long cpu; #ifdef SOLARIS if (times(&buffer) != -1) cpu = (buffer.tms_utime + buffer.tms_stime); #else if (getrusage(RUSAGE_SELF, &rus) >= 0) { cpu = rus.ru_utime.tv_sec * 1000 + rus.ru_utime.tv_usec / 1000 + rus.ru_stime.tv_sec * 1000 + rus.ru_stime.tv_usec / 1000; } #endif else cpu = (-1); if (progcpui >= MAX_CPU_STACK) fatal("CPU-Stack overflow.\n"); progcpu[progcpui].prog = current_prog; progcpu[progcpui++].cpu = cpu; } #endif } static void f_call_selfv(int num_arg) { static void f_call_self(); struct vector *argv = sp->u.vec; int i; argv->ref++; pop_stack(); num_arg = argv->size + 1; for(i = 0; i < argv->size; i++) { push_svalue(&argv->item[i]); } free_vector(argv); f_call_self(num_arg); } static void f_call_self(int num_arg) { struct function *funp; struct svalue *arg; arg = sp - num_arg + 1; if (search_for_function(arg->u.string, current_object->prog) == 0 || function_type_mod_found & TYPE_MOD_PRIVATE && inh_offset < function_inherit_found - function_prog_found->num_inherited + 1) { /* No such function */ pop_n_elems(num_arg); push_number(0); return; } free_svalue(arg); num_arg--; memmove(arg, &arg[1], num_arg * sizeof(struct svalue)); access_program(function_prog_found); funp = &(function_prog_found->functions[function_index_found]); /* Urgle. There should probably be a function for all this. |D| * See call-self for comments */ push_control_stack (funp); inh_offset = function_inherit_found; current_prog = function_prog_found; csp->ext_call = 0; csp->num_local_variables = num_arg; pc = setup_new_frame(funp); csp->extern_call = 0; #ifdef RUSAGE { #ifdef SOLARIS struct tms buffer; #else struct rusage rus; #endif long cpu; #ifdef SOLARIS if (times(&buffer) != -1) cpu = (buffer.tms_utime + buffer.tms_stime); #else if (getrusage(RUSAGE_SELF, &rus) >= 0) { cpu = rus.ru_utime.tv_sec * 1000 + rus.ru_utime.tv_usec / 1000 + rus.ru_stime.tv_sec * 1000 + rus.ru_stime.tv_usec / 1000; } #endif else cpu = (-1); if (progcpui >= MAX_CPU_STACK) fatal("CPU-Stack overflow.\n"); progcpu[progcpui].prog = current_prog; progcpu[progcpui++].cpu = cpu; } #endif } static void f_call_non_virt(int num_arg) { /* Receives: char index into inherit-list * short function name (index into program strings). * char number of arguments */ struct function *funp; int inh, num_args; unsigned short fix, fiix, fnix; #ifdef COUNT_CALLS num_call_down++; #endif fiix = EXTRACT_UCHAR(pc); pc++; ((char *)&fix)[0] = pc[0]; ((char *)&fix)[1] = pc[1]; pc += 2; num_args = EXTRACT_UCHAR(pc); pc++; inh = fiix - (current_prog->num_inherited - 1); function_prog_found = current_prog->inherit[fiix].prog; access_program(function_prog_found); funp = &(function_prog_found->functions[fix]); /* Urgle. There should probably be a function for all this. |D| * See call-self for comments */ push_control_stack (funp); inh_offset += inh; current_prog = function_prog_found; csp->ext_call = 0; csp->num_local_variables = num_args; pc = setup_new_frame(funp); csp->extern_call = 0; #ifdef RUSAGE { #ifdef SOLARIS struct tms buffer; #else struct rusage rus; #endif long cpu; #ifdef SOLARIS if (times(&buffer) != -1) cpu = (buffer.tms_utime + buffer.tms_stime); #else if (getrusage(RUSAGE_SELF, &rus) >= 0) { cpu = rus.ru_utime.tv_sec * 1000 + rus.ru_utime.tv_usec / 1000 + rus.ru_stime.tv_sec * 1000 + rus.ru_stime.tv_usec / 1000; } #endif else cpu = (-1); if (progcpui >= MAX_CPU_STACK) fatal("CPU-Stack overflow.\n"); progcpu[progcpui].prog = current_prog; progcpu[progcpui++].cpu = cpu; } #endif } static void f_call_c(int num_arg) { void (*func)(); int f1 = 0; #ifdef __alpha int f2 = 0; #endif ((char *)&f1)[0] = pc[0]; ((char *)&f1)[1] = pc[1]; ((char *)&f1)[2] = pc[2]; ((char *)&f1)[3] = pc[3]; pc += 4; #ifdef __alpha ((char *)&f2)[0] = pc[0]; ((char *)&f2)[1] = pc[1]; ((char *)&f2)[2] = pc[2]; ((char *)&f2)[3] = pc[3]; pc += 4; func = (void (*)())((long)f1 | ((long)f2 << 32)); #else func = (void (*)())f1; #endif func(fp); } static void f_call_simul(int num_arg) { extern char *simul_efun_file_name; unsigned short func_name_index; int num_args, suc = 0; char *func; pc++; ((char *)&func_name_index)[0] = pc[0]; ((char *)&func_name_index)[1] = pc[1]; pc += 2; num_args = EXTRACT_UCHAR(pc); pc++; func = current_prog->rodata + func_name_index; suc = 0; if (!simul_efun_file_name || current_prog->name == simul_efun_file_name || !apply_low(func, (struct object *)query_simul_efun_ob(), num_args, 1)) { char buff[200]; sprintf (buff, "Simulated efun %s not found", current_prog->rodata + func_name_index); error (buff); } } static void f_previous_object(int num_arg) { int n; struct control_stack *cspi; if (sp->u.number > 0 || (sp->u.number == 0 && (previous_ob == 0 || (previous_ob->flags & O_DESTRUCTED)))) { pop_stack(); push_number(0); return; } else if (sp->u.number == 0) { pop_stack(); push_object(previous_ob); return; } n = sp->u.number; pop_stack(); for (cspi = csp; n && cspi > control_stack; cspi--) if (cspi->ext_call) n++; if (cspi == control_stack || cspi->ob == 0 || cspi->ob->flags & O_DESTRUCTED) push_number(0); else push_object(cspi->ob); } static void f_calling_program(int num_arg) { int n = sp->u.number; struct control_stack *cspi; pop_stack(); if (n > 0 || -n > MAX_TRACE) { push_number(0); return; } cspi = csp + n; if (cspi <= control_stack || cspi->prog == 0) push_number(0); else push_string(cspi->prog->name, STRING_MALLOC); } static void f_calling_object(int num_arg) { int n = sp->u.number; struct control_stack *cspi; pop_stack(); if (n > 0 || -n > MAX_TRACE) { push_number(0); return; } cspi = csp + n; if (cspi <= control_stack || cspi->ob == 0 || cspi->ob->flags & O_DESTRUCTED) push_number(0); else push_object(cspi->ob); } static void f_calling_function(int num_arg) { int n = sp->u.number; struct control_stack *cspi; pop_stack(); if (n > 0 || -n > MAX_TRACE) { push_number(0); return; } cspi = csp + n - 1; if (cspi < control_stack) push_number(0); else if (cspi->funp) push_string(cspi->funp->name, STRING_MALLOC); else push_string("<internal>", STRING_CONSTANT); } static void f_store(int num_arg) { fatal("f_store should not be called.\n"); } static void f_if(int num_arg) { fatal("f_if should not be called.\n"); } static void f_land(int num_arg) { fatal("f_land should not be called.\n"); } static void f_lor(int num_arg) { fatal("f_lor should not be called.\n"); } static void f_status(int num_arg) { fatal("f_status should not be called.\n"); } static void f_comma(int num_arg) { fatal("f_comma should not be called.\n"); } static void f_int(int num_arg) { fatal("f_int should not be called.\n"); } static void f_string_decl(int num_arg) { fatal("f_string_decl should not be called.\n"); } static void f_else(int num_arg) { fatal("f_else should not be called.\n"); } static void f_describe(int num_arg) { fatal("f_describe should not be called.\n"); } static void f_continue(int num_arg) { fatal("f_continue should not be called.\n"); } static void f_inherit(int num_arg) { fatal("f_inherit should not be called.\n"); } static void f_colon_colon(int num_arg) { fatal("f_colon_colon should not be called.\n"); } static void f_static(int num_arg) { fatal("f_static should not be called.\n"); } static void f_arrow(int num_arg) { fatal("f_arrow should not be called.\n"); } static void f_object(int num_arg) { fatal("f_object should not be called.\n"); } static void f_void(int num_arg) { fatal("f_void should not be called.\n"); } static void f_mixed(int num_arg) { fatal("f_mixed should not be called.\n"); } static void f_private(int num_arg) { fatal("f_private should not be called.\n"); } static void f_no_mask(int num_arg) { fatal("f_no_mask should not be called.\n"); } static void f_mapping(int num_arg) { fatal("f_mapping should not be called.\n"); } static void f_float(int num_arg) { fatal("f_float should not be called.\n"); } static void f_protected(int num_arg) { fatal("f_protected should not be called.\n"); } static void f_public(int num_arg) { fatal("f_public should not be called.\n"); } static void f_varargs(int num_arg) { fatal("f_varargs should not be called.\n"); } static void f_vararg(int num_arg) { fatal("f_vararg should not be called.\n"); } static void f_case(int num_arg) { fatal("f_case should not be called.\n"); } static void f_default(int num_arg) { fatal("f_default should not be called.\n"); } static void f_itof(int num_arg) { sp->type = T_FLOAT; sp->u.real = sp->u.number; } static void f_ftoi(int num_arg) { sp->type = T_NUMBER; sp->u.number = sp->u.real; } static void f_sin(int num_arg) { sp->u.real = sin(sp->u.real); } static void f_cos(int num_arg) { sp->u.real = cos(sp->u.real); } static void f_tan(int num_arg) { sp->u.real = tan(sp->u.real); } static void f_asin(int num_arg) { if (fabs(sp->u.real) > 1.0) error("Argument out of bounds to asin()"); sp->u.real = asin(sp->u.real); } static void f_acos(int num_arg) { if (fabs(sp->u.real) > 1.0) error("Argument out of bounds to acos()"); sp->u.real = acos(sp->u.real); } static void f_atan(int num_arg) { sp->u.real = atan(sp->u.real); } static void f_atan2(int num_arg) { (sp-1)->u.real = atan2((sp-1)->u.real, sp->u.real); sp--; } static void f_exp(int num_arg) { sp->u.real = exp(sp->u.real); } static void f_log(int num_arg) { sp->u.real = log(sp->u.real); } static void f_pow(int num_arg) { (sp-1)->u.real = pow((sp-1)->u.real, sp->u.real); sp--; } static void f_sinh(int num_arg) { sp->u.real = sinh(sp->u.real); } static void f_cosh(int num_arg) { sp->u.real = cosh(sp->u.real); } static void f_tanh(int num_arg) { sp->u.real = tanh(sp->u.real); } #ifdef F_ASINH static void f_asinh(int num_arg) { sp->u.real = asinh(sp->u.real); } #endif #ifdef F_ACOSH static void f_acosh(int num_arg) { if (sp->u.real < 1.0) error("Argument out of bounds to acosh()"); sp->u.real = acosh(sp->u.real); } #endif #ifdef F_ATANH static void f_atanh(int num_arg) { if (fabs(sp->u.real) > 1.0) error("Argument out of bounds to atanh()"); sp->u.real = atanh(sp->u.real); } #endif static void f_abs(int num_arg) { sp->u.real = fabs(sp->u.real); } static void f_fact(int num_arg) { if ( sp->u.real < 0.0) error("Argument out of bounds to fact()"); #ifdef ns32000 sp->u.real = exp(gamma(sp->u.real + 1.0)); #else sp->u.real = exp(lgamma(sp->u.real + 1.0)); #endif } static void f_rnd(int num_arg) { extern double random_float(); push_float(random_float()); } static void f_ftoa(int num_arg) { char buffer[1024]; sprintf(buffer,"%.8g",sp->u.real); sp--; push_string(buffer, STRING_MALLOC); } static void f_floatc(int num_arg) { float f; ((char *)&f)[0] = pc[0]; ((char *)&f)[1] = pc[1]; ((char *)&f)[2] = pc[2]; ((char *)&f)[3] = pc[3]; pc += 4; push_float(f); } static void f_regexp(int num_arg) { struct vector *v; v = match_regexp((sp-1)->u.vec, sp->u.string); pop_n_elems(2); if (v == 0) push_number(0); else { push_vector(v); v->ref--; /* Will make ref count == 1 */ } } static void f_shadow(int num_arg) { struct object *ob; ob = (sp-1)->u.ob; if (sp->u.number == 0) { ob = ob->shadowed; pop_n_elems(2); if (ob) push_object(ob); else push_number(0); return; } if (validate_shadowing(ob)) { /* * The shadow is entered first in the chain. */ while (ob->shadowed) ob = ob->shadowed; change_ref(current_object->shadowing, ob, "f_shadow-1"); current_object->shadowing = ob; change_ref(ob->shadowed, current_object, "f_shadow-2"); ob->shadowed = current_object; pop_n_elems(2); push_object(ob); return; } pop_n_elems(2); push_number(0); } static void f_pop_value(int num_arg) { pop_stack(); } static void f_dup(int num_arg) { sp++; assign_svalue_no_free(sp, sp-1); } static void f_jump_when_zero(int num_arg) { unsigned short offset; ((char *)&offset)[0] = pc[0]; ((char *)&offset)[1] = pc[1]; if (sp->type == T_NUMBER && sp->u.number == 0) pc = current_prog->program + offset; else pc += 2; pop_stack(); } static void f_jump(int num_arg) { unsigned short offset; ((char *)&offset)[0] = pc[0]; ((char *)&offset)[1] = pc[1]; pc = current_prog->program + offset; } static void f_jump_when_non_zero(int num_arg) { unsigned short offset; ((char *)&offset)[0] = pc[0]; ((char *)&offset)[1] = pc[1]; if (sp->type == T_NUMBER && sp->u.number == 0) pc += 2; else pc = current_prog->program + offset; pop_stack(); } static void f_indirect(int num_arg) { #ifdef DEBUG if (sp->type != T_LVALUE) fatal("Bad type to F_INDIRECT\n"); #endif assign_svalue(sp, sp->u.lvalue); /* * Fetch value of a variable. It is possible that it is a variable * that points to a destructed object. In that case, it has to * be replaced by 0. */ if (sp->type == T_OBJECT && (sp->u.ob->flags & O_DESTRUCTED)) { free_svalue(sp); *sp = const0; } } static void f_identifier(int num_arg) { sp++; assign_svalue_no_free(sp, find_value((int)EXTRACT_UCHAR(pc), (int)EXTRACT_UCHAR(pc + 1))); pc += 2; /* * Fetch value of a variable. It is possible that it is a variable * that points to a destructed object. In that case, it has to * be replaced by 0. */ if (sp->type == T_OBJECT && (sp->u.ob->flags & O_DESTRUCTED)) { free_svalue(sp); *sp = const0; } } static void f_push_identifier_lvalue(int num_arg) { sp++; sp->type = T_LVALUE; sp->u.lvalue = find_value((int)EXTRACT_UCHAR(pc),(int)EXTRACT_UCHAR(pc + 1)); pc += 2; } static void f_push_indexed_lvalue(int num_arg) { push_indexed_lvalue(1); } static void f_index(int num_arg) { push_indexed_lvalue(0); assign_svalue_no_free(sp, sp->u.lvalue); /* * Fetch value of a variable. It is possible that it is a variable * that points to a destructed object. In that case, it has to * be replaced by 0. */ if (sp->type == T_OBJECT && (sp->u.ob->flags & O_DESTRUCTED)) { free_svalue(sp); sp->type = T_NUMBER; sp->u.number = 0; } } static void f_local_name(int num_arg) { sp++; assign_svalue_no_free(sp, fp + EXTRACT_UCHAR(pc)); pc++; /* * Fetch value of a variable. It is possible that it is a variable * that points to a destructed object. In that case, it has to * be replaced by 0. */ if (sp->type == T_OBJECT && (sp->u.ob->flags & O_DESTRUCTED)) { free_svalue(sp); *sp = const0; } } static void f_push_local_variable_lvalue(int num_arg) { sp++; sp->type = T_LVALUE; sp->u.lvalue = fp + EXTRACT_UCHAR(pc); pc++; } static void f_return(int num_arg) { fatal("f_return should not be called.\n"); } static void f_break_point(int num_arg) { break_point(); } static void f_break_string(int num_arg) { struct svalue *arg = sp- num_arg + 1; char *str; if (arg[0].type == T_STRING) { str = break_string(arg[0].u.string, arg[1].u.number, (num_arg > 2 ? &arg[2] : (struct svalue *)0)); pop_n_elems(num_arg); push_malloced_string(str); } else { pop_n_elems(num_arg); push_number(0); } } static void f_clone_object(int num_arg) { struct object *ob; ob = clone_object(sp->u.string); pop_stack(); if (ob) { sp++; sp->type = T_OBJECT; sp->u.ob = ob; add_ref(ob, "F_CLONE_OBJECT"); } else push_number(0); } static void f_aggregate(int num_arg) { struct vector *v; unsigned short num; int i; ((char *)&num)[0] = pc[0]; ((char *)&num)[1] = pc[1]; pc += 2; v = allocate_array((int)num); for (i = 0; i < (int)num; i++) assign_svalue_no_free(&v->item[i], sp + i - num + 1); pop_n_elems((int)num); sp++; sp->type = T_POINTER; sp->u.vec = v; /* Ref count already initialized */ } static void f_m_aggregate(int num_arg) { struct mapping *m; unsigned short num; struct svalue *arg; int i; ((char *)&num)[0] = pc[0]; ((char *)&num)[1] = pc[1]; pc += 2; m = allocate_map((int)num); /* Ref count = 1 */ for (i = 0 ; i < (int)num ; i += 2) { arg = sp + i - num; assign_svalue(get_map_lvalue(m, arg + 1, 1), arg + 2); } pop_n_elems((int)num); sp++; sp->type = T_MAPPING; sp->u.map = m; /* Ref count already initialized */ } static void f_tail(int num_arg) { if (tail(sp->u.string)) assign_svalue(sp, &const1); else assign_svalue(sp, &const0); } static void f_save_map(int num_arg) { save_map(current_object, (sp - 1)->u.map, sp->u.string); pop_stack(); } static void f_save_object(int num_arg) { save_object(current_object, sp->u.string); /* The argument is returned */ } static void f_m_save_object(int num_arg) { push_mapping(m_save_object(current_object)); sp->u.map->ref--; return; } static void f_find_object(int num_arg) { struct object *ob; ob = find_object2(sp->u.string); pop_stack(); if (ob) push_object(ob); else push_number(0); } static void f_write_file(int num_arg) { int i; i = write_file((sp-1)->u.string, sp->u.string); pop_n_elems(2); push_number(i); } static void f_read_file(int num_arg) { char *str; struct svalue *arg = sp- num_arg + 1; int start = 0, len = 0; if (num_arg > 1) start = arg[1].u.number; if (num_arg == 3) { if (arg[2].type != T_NUMBER) bad_arg(2, F_READ_FILE, &arg[2]); len = arg[2].u.number; } str = read_file(arg[0].u.string, start, len); pop_n_elems(num_arg); if (str == 0) push_number(0); else { push_malloced_string(str); } } static void f_read_bytes(int num_arg) { char *str; struct svalue *arg = sp- num_arg + 1; int start = 0, len = 0; if (num_arg > 1) start = arg[1].u.number; if (num_arg == 3) { if (arg[2].type != T_NUMBER) bad_arg(2, F_READ_BYTES, &arg[2]); len = arg[2].u.number; } str = read_bytes(arg[0].u.string, start, len); pop_n_elems(num_arg); if (str == 0) push_number(0); else { push_malloced_string(str); } } static void f_write_bytes(int num_arg) { int i; i = write_bytes((sp-2)->u.string, (sp-1)->u.number, sp->u.string); pop_n_elems(3); push_number(i); } static void f_file_size(int num_arg) { int i; i = file_size(sp->u.string); pop_stack(); push_number(i); } static void f_file_time(int num_arg) { int i; i = file_time(sp->u.string); pop_stack(); push_number(i); } static void f_find_living(int num_arg) { struct object *ob; ob = find_living_object(sp->u.string); pop_stack(); if (ob) push_object(ob); else push_number(0); } static void f_write_socket(int num_arg) { if (sp->type == T_NUMBER) { char tmpbuf[10]; sprintf(tmpbuf, "%d", sp->u.number); if (current_object->interactive) write_socket(tmpbuf, current_object); else if (current_object == master_ob) write_socket(tmpbuf, 0); } else { if (current_object->interactive) write_socket(sp->u.string, current_object); else if (current_object == master_ob) write_socket(sp->u.string, 0); } } static void f_str2val(int num_arg) { struct svalue sval = *sp; char *str = sval.u.string; *sp = const0; restore_one(sp, &str); free_svalue(&sval); } static void f_val2str(int num_arg) { extern char *valtostr(struct svalue *); char *ret; ret = valtostr(sp); pop_stack(); push_malloced_string(ret); return; } static void f_restore_map(int num_arg) { struct mapping *map = allocate_map(0); restore_map(current_object, map, sp->u.string); pop_stack(); push_mapping(map); map->ref--; /* to make ref == 1 */ } static void f_restore_object(int num_arg) { int i; i = restore_object(current_object, sp->u.string); pop_stack(); push_number(i); } static void f_m_restore_object(int num_arg) { int i; i = m_restore_object(current_object, sp->u.map); pop_stack(); push_number(i); } static void f_this_interactive(int num_arg) { if (current_interactive && !(current_interactive->flags & O_DESTRUCTED)) push_object(current_interactive); else push_number(0); } static void f_this_player(int num_arg) { if (command_giver && !(command_giver->flags & O_DESTRUCTED)) push_object(command_giver); else push_number(0); } static void f_set_this_player(int num_arg) { if (sp->type == T_NUMBER) { if (sp->u.number != 0) error("Bad argument 1 to set_this_player()"); command_giver = 0; } else if (sp->u.ob->flags & O_ENABLE_COMMANDS) command_giver = sp->u.ob; } static void f_living(int num_arg) { if (sp->type == T_NUMBER) { assign_svalue(sp, &const0); return; } if (sp->u.ob->flags & O_ENABLE_COMMANDS) assign_svalue(sp, &const1); else assign_svalue(sp, &const0); } static void f_set_auth(int num_arg) { struct svalue *ret = 0; struct svalue *arg = sp - 1; if (master_ob) { push_object(current_object); push_object(arg->u.ob); push_svalue(arg + 1); ret = apply_master_ob(M_VALID_SET_AUTH, 3); } if (!ret) { pop_n_elems(2); push_number(0); return; } access_object(arg->u.ob); assign_svalue(&(arg->u.ob->variables[-1]), ret); access_object(current_object); pop_n_elems(2); push_number(0); } static void f_query_auth(int num_arg) { int i; struct object *ob = sp->u.ob; pop_stack(); access_object(ob); switch(ob->variables[-1].type) { case T_POINTER: push_vector(allocate_array(ob->variables[-1].u.vec->size)); for(i = 0; i < ob->variables[-1].u.vec->size; i++) assign_svalue_no_free(&(sp->u.vec->item[i]), &(ob->variables[-1].u.vec->item[i])); sp->u.vec--; break; case T_MAPPING: push_mapping(copy_mapping(ob->variables[-1].u.map)); sp->u.map->ref--; break; default: push_svalue(&(ob->variables[-1])); } access_object(current_object); } static void f_explode(int num_arg) { struct vector *v; v = explode_string((sp-1)->u.string, sp->u.string); pop_n_elems(2); if (v) { push_vector(v); /* This will make ref count == 2 */ v->ref--; } else { push_number(0); } } static void f_filter(int num_arg) { struct vector *v; struct svalue *arg; struct mapping *m; struct object *ob; arg = sp - num_arg + 1; ob = 0; if (arg[2].type == T_OBJECT) ob = arg[2].u.ob; else if (arg[2].type == T_STRING) ob = find_object(arg[2].u.string); if (!ob) error("Bad third argument to filter_array()\n"); if (arg[0].type == T_POINTER) { check_for_destr(&arg[0]); v = filter_arr(arg[0].u.vec, arg[1].u.string, ob, num_arg > 3 ? sp : (struct svalue *)0); } else { v = 0; } if (arg[0].type == T_MAPPING) { check_for_destr(&arg[0]); m = filter_map(arg[0].u.map, arg[1].u.string, ob, num_arg >3 ? sp : (struct svalue *)0); } else { m = 0; } pop_n_elems(num_arg); if (v) { push_vector(v); /* This will make ref count == 2 */ v->ref--; } else if (m) { push_mapping(m); /* This will make ref count == 2 */ m->ref--; } else { push_number(0); } } static void f_set_bit(int num_arg) { char *str; int len, old_len, ind; if (sp->u.number > MAX_BITS) error("set_bit: too big bit number: %d\n", sp->u.number); len = strlen((sp-1)->u.string); old_len = len; ind = sp->u.number/6; if (ind >= len) len = ind + 1; str = xalloc(len+1); str[len] = '\0'; if (old_len) memcpy(str, (sp-1)->u.string, old_len); if (len > old_len) memset(str + old_len, ' ', len - old_len); if (str[ind] > 0x3f + ' ' || str[ind] < ' ') error("Illegal bit pattern in set_bit character %d\n", ind); str[ind] = ((str[ind] - ' ') | (1 << (sp->u.number % 6))) + ' '; pop_n_elems(2); sp++; sp->u.string = str; sp->string_type = STRING_MALLOC; sp->type = T_STRING; } static void f_clear_bit(int num_arg) { char *str; int len, ind; if (sp->u.number > MAX_BITS) error("clear_bit: too big bit number: %d\n", sp->u.number); len = strlen((sp-1)->u.string); ind = sp->u.number/6; if (ind >= len) { /* Return first argument unmodified ! */ pop_stack(); return; } str = xalloc(len+1); memcpy(str, (sp-1)->u.string, len+1); /* Including null byte */ if (str[ind] > 0x3f + ' ' || str[ind] < ' ') error("Illegal bit pattern in clear_bit character %d\n", ind); str[ind] = ((str[ind] - ' ') & ~(1 << sp->u.number % 6)) + ' '; pop_n_elems(2); sp++; sp->type = T_STRING; sp->string_type = STRING_MALLOC; sp->u.string = str; } static void f_test_bit(int num_arg) { int len; len = strlen((sp-1)->u.string); if (sp->u.number/6 >= len) { pop_n_elems(2); push_number(0); return; } if (((sp-1)->u.string[sp->u.number/6] - ' ') & 1 << sp->u.number % 6) { pop_n_elems(2); push_number(1); } else { pop_n_elems(2); push_number(0); } } static void f_catch(int num_arg) { fatal("f_catch should not be called.\n"); } static void f_throw(int num_arg) { /* marion * the return from catch is now done by a 0 throw */ assign_svalue(&catch_value, sp); free_svalue(sp--); if (catch_value.type == T_NUMBER && catch_value.u.number == 0) { /* We come here when no longjmp() was executed. */ push_pop_error_context (0); push_number(0); } else throw_error(); /* do the longjump, with extra checks... */ } static void f_notify_fail(int num_arg) { set_notify_fail_message(sp->u.string); /* Return 0 */ pop_stack(); push_number(0); } static void f_query_idle(int num_arg) { int i; i = query_idle(sp->u.ob); pop_stack(); push_number(i); } static void f_implode(int num_arg) { char *str; if ((sp-1)->type == T_NUMBER) { pop_stack(); return; } check_for_destr(sp-1); str = implode_string((sp-1)->u.vec, sp->u.string); pop_n_elems(2); if (str) { sp++; sp->type = T_STRING; sp->string_type = STRING_MALLOC; sp->u.string = str; } else { push_number(0); } } static void f_query_snoop(int num_arg) { struct object *ob; if (current_object == master_ob && sp->u.ob->interactive) ob = query_snoop(sp->u.ob); else ob = 0; pop_stack(); if (ob) push_object(ob); else push_number(0); } static void f_query_ip_number_name(int name, int num_arg) { char *tmp; if (num_arg == 1 && sp->type != T_OBJECT) error("Bad optional argument to query_ip_number()\n"); if (name) tmp = query_ip_name(num_arg ? sp->u.ob : 0); else tmp = query_ip_number(num_arg ? sp->u.ob : 0); if (num_arg) pop_stack(); if (tmp == 0) push_number(0); else push_string(tmp, STRING_MALLOC); } static void f_query_ip_number(int num_arg) { f_query_ip_number_name(0, num_arg); } static void f_query_ip_name(int num_arg) { f_query_ip_number_name(1, num_arg); } static void f_query_ip_ident(int num_arg) { struct object *ob = sp->u.ob; pop_stack(); if (!ob->interactive || !ob->interactive->rname) push_number(0); else push_string(ob->interactive->rname, STRING_MALLOC); return; } static void f_query_host_name(int num_arg) { extern char *query_host_name(); char *tmp; tmp = query_host_name(); if (tmp) push_string(tmp, STRING_MALLOC); else push_number(0); } static void f_all_inventory(int num_arg) { struct vector *vec; vec = all_inventory(sp->u.ob); pop_stack(); if (vec == 0) { push_number(0); } else { push_vector(vec); /* This will make ref count == 2 */ vec->ref--; } } static void f_deep_inventory(int num_arg) { struct vector *vec; if (sp->type == T_NUMBER) vec = allocate_array(0); else vec = deep_inventory(sp->u.ob, 0); free_svalue(sp); sp->type = T_POINTER; sp->u.vec = vec; } static void f_environment(int num_arg) { struct object *ob; ob = environment(sp); pop_stack(); if (ob) push_object(ob); else push_number(0); } static void f_this_object(int num_arg) { push_object(current_object); } static void f_object_clones(int num_arg) { struct vector *v; struct object *ob; int i; if (sp->type == T_NUMBER) v = allocate_array(0); else { ob = obj_list; i = 0; do { if (ob->prog == sp->u.ob->prog && (ob->flags & O_CLONE)) i++; } while ((ob = ob->next_all) != obj_list); v = allocate_array(i); ob = obj_list; i = 0; do if (ob->prog == sp->u.ob->prog && (ob->flags & O_CLONE)) { v->item[i].type = T_OBJECT; v->item[i++].u.ob = ob; add_ref(ob,"object_clones"); } while ((ob = ob->next_all) != obj_list) ; } pop_stack(); push_vector(v); v->ref--; /* Refcount == 2 after push */ } static void f_commands(int num_arg) { struct vector *vec; if (sp->type == T_NUMBER) vec = allocate_array(0); else vec = get_local_commands(sp->u.ob); pop_stack(); push_vector(vec); vec->ref--; /* Refcount = 2 after the push */ } static void f_time(int num_arg) { push_number(current_time); } static void f_add(int num_arg) { int i; /*if (inadd==0) checkplus(p);*/ if ((sp-1)->type == T_STRING && sp->type == T_STRING) { char *res; int l = strlen((sp-1)->u.string); res = xalloc(l + strlen(sp->u.string) + 1); (void)strcpy(res, (sp-1)->u.string); (void)strcpy(res+l, sp->u.string); pop_n_elems(2); push_malloced_string(res); } else if ((sp-1)->type == T_NUMBER && sp->type == T_STRING) { char buff[20], *res; sprintf(buff, "%d", (sp-1)->u.number); res = xalloc(strlen(sp->u.string) + strlen(buff) + 1); strcpy(res, buff); strcat(res, sp->u.string); pop_n_elems(2); push_malloced_string(res); } else if (sp->type == T_NUMBER && (sp-1)->type == T_STRING) { char buff[20]; char *res; sprintf(buff, "%d", sp->u.number); res = xalloc(strlen((sp-1)->u.string) + strlen(buff) + 1); strcpy(res, (sp-1)->u.string); strcat(res, buff); pop_n_elems(2); push_malloced_string(res); } else if ((sp-1)->type == T_NUMBER && sp->type == T_NUMBER) { i = sp->u.number + (sp-1)->u.number; sp--; sp->u.number = i; } else if ((sp-1)->type == T_FLOAT && sp->type == T_FLOAT) { (sp-1)->u.real += sp->u.real; sp--; } else if ((sp-1)->type == T_POINTER && sp->type == T_POINTER) { struct vector *v; check_for_destr(sp-1); check_for_destr(sp); v = add_array((sp-1)->u.vec,sp->u.vec); pop_n_elems(2); push_vector(v); /* This will make ref count == 2 */ v->ref--; } else if ((sp-1)->type == T_MAPPING && sp->type == T_MAPPING) { struct mapping *m; check_for_destr(sp-1); check_for_destr(sp); m = add_mapping((sp-1)->u.map, sp->u.map); pop_n_elems(2); push_mapping(m); /* This will make ref count == 2 */ m->ref--; } else { error("Bad type of arg to '+'\n"); } } static void f_subtract(int num_arg) { int i; if ((sp-1)->type == T_POINTER && sp->type == T_POINTER) { struct vector *v; v = subtract_array((sp-1)->u.vec, sp->u.vec); pop_stack(); pop_stack(); if (v == 0) { push_number(0); } else { push_vector(v); /* This will make ref count == 2 */ v->ref--; } return; } if ((sp-1)->type == T_FLOAT && sp->type == T_FLOAT) { (sp-1)->u.real -= sp->u.real; sp--; return; } if ((sp-1)->type != T_NUMBER) bad_arg(1, F_SUBTRACT, sp-1); if (sp->type != T_NUMBER) bad_arg(2, F_SUBTRACT, sp); i = (sp-1)->u.number - sp->u.number; sp--; sp->u.number = i; } static void f_and(int num_arg) { int i; if (sp->type == T_POINTER && (sp-1)->type == T_POINTER) { struct vector *v; v = intersect_array(sp->u.vec, (sp-1)->u.vec); pop_stack(); pop_stack(); if (v == 0) { push_number(0); } else { push_vector(v); /* This will make ref count == 2 */ v->ref--; } return; } if ((sp-1)->type != T_NUMBER) bad_arg(1, F_AND, sp-1); if (sp->type != T_NUMBER) bad_arg(2, F_AND, sp); i = (sp-1)->u.number & sp->u.number; sp--; sp->u.number = i; } static void f_or(int num_arg) { int i; if ((sp-1)->type != T_NUMBER) bad_arg(1, F_OR, sp-1); if (sp->type != T_NUMBER) bad_arg(2, F_OR, sp); i = (sp-1)->u.number | sp->u.number; sp--; sp->u.number = i; } static void f_xor(int num_arg) { int i; if ((sp-1)->type != T_NUMBER) bad_arg(1, F_XOR, sp-1); if (sp->type != T_NUMBER) bad_arg(2, F_XOR, sp); i = (sp-1)->u.number ^ sp->u.number; sp--; sp->u.number = i; } static void f_lsh(int num_arg) { int i; if ((sp-1)->type != T_NUMBER) bad_arg(1, F_LSH, sp-1); if (sp->type != T_NUMBER) bad_arg(2, F_LSH, sp); i = (sp-1)->u.number << sp->u.number; sp--; sp->u.number = i; } static void f_rsh(int num_arg) { int i; if ((sp-1)->type != T_NUMBER) bad_arg(1, F_RSH, sp-1); if (sp->type != T_NUMBER) bad_arg(2, F_RSH, sp); i = (sp-1)->u.number >> sp->u.number; sp--; sp->u.number = i; } static void f_multiply(int num_arg) { int i; if ((sp-1)->type == T_FLOAT && sp->type == T_FLOAT) { (sp-1)->u.real *= sp->u.real; sp--; return; } if ((sp-1)->type != T_NUMBER) bad_arg(1, F_MULTIPLY, sp-1); if (sp->type != T_NUMBER) bad_arg(2, F_MULTIPLY, sp); i = (sp-1)->u.number * sp->u.number; sp--; sp->u.number = i; } static void f_divide(int num_arg) { int i; if ((sp-1)->type == T_FLOAT && sp->type == T_FLOAT) { if (sp->u.real == 0.0) error("Division by zero\n"); (sp-1)->u.real /= sp->u.real; sp--; return; } if ((sp-1)->type != T_NUMBER) bad_arg(1, F_DIVIDE, sp-1); if (sp->type != T_NUMBER) bad_arg(2, F_DIVIDE, sp); if (sp->u.number == 0) error("Division by zero\n"); i = (sp-1)->u.number / sp->u.number; sp--; sp->u.number = i; } static void f_mod(int num_arg) { int i; if ((sp-1)->type != T_NUMBER) bad_arg(1, F_MOD, sp-1); if (sp->type != T_NUMBER) bad_arg(2, F_MOD, sp); if (sp->u.number == 0) error("Modulus by zero.\n"); i = (sp-1)->u.number % sp->u.number; sp--; sp->u.number = i; } static void f_gt(int num_arg) { int i; if ((sp-1)->type == T_STRING && sp->type == T_STRING) { i = strcmp((sp-1)->u.string, sp->u.string) > 0; pop_n_elems(2); push_number(i); return; } if ((sp-1)->type == T_FLOAT && sp->type == T_FLOAT) { (sp-1)->u.number = (sp-1)->u.real > sp->u.real; sp--; sp->type = T_NUMBER; return; } if ((sp-1)->type != T_NUMBER) bad_arg(1, F_GT, sp-1); if (sp->type != T_NUMBER) bad_arg(2, F_GT, sp); i = (sp-1)->u.number > sp->u.number; sp--; sp->u.number = i; } static void f_ge(int num_arg) { int i; if ((sp-1)->type == T_STRING && sp->type == T_STRING) { i = strcmp((sp-1)->u.string, sp->u.string) >= 0; pop_n_elems(2); push_number(i); return; } if ((sp-1)->type == T_FLOAT && sp->type == T_FLOAT) { (sp-1)->u.number = (sp-1)->u.real >= sp->u.real; sp--; sp->type = T_NUMBER; return; } if ((sp-1)->type != T_NUMBER) bad_arg(1, F_GE, sp-1); if (sp->type != T_NUMBER) bad_arg(2, F_GE, sp); i = (sp-1)->u.number >= sp->u.number; sp--; sp->u.number = i; } static void f_lt(int num_arg) { int i; if ((sp-1)->type == T_STRING && sp->type == T_STRING) { i = strcmp((sp-1)->u.string, sp->u.string) < 0; pop_n_elems(2); push_number(i); return; } if ((sp-1)->type == T_FLOAT && sp->type == T_FLOAT) { (sp-1)->u.number = (sp-1)->u.real < sp->u.real; sp--; sp->type = T_NUMBER; return; } if ((sp-1)->type != T_NUMBER) bad_arg(1, F_LT, sp-1); if (sp->type != T_NUMBER) bad_arg(2, F_LT, sp); i = (sp-1)->u.number < sp->u.number; sp--; sp->u.number = i; } static void f_le(int num_arg) { int i; if ((sp-1)->type == T_STRING && sp->type == T_STRING) { i = strcmp((sp-1)->u.string, sp->u.string) <= 0; pop_n_elems(2); push_number(i); return; } if ((sp-1)->type == T_FLOAT && sp->type == T_FLOAT) { (sp-1)->u.number = (sp-1)->u.real <= sp->u.real; sp--; sp->type = T_NUMBER; return; } if ((sp-1)->type != T_NUMBER) bad_arg(1, F_LE, sp-1); if (sp->type != T_NUMBER) bad_arg(2, F_LE, sp); i = (sp-1)->u.number <= sp->u.number; sp--; sp->u.number = i; } static void f_eq(int num_arg) { int i; if ((sp-1)->type != sp->type) { pop_stack(); assign_svalue(sp, &const0); return; } switch(sp->type) { case T_NUMBER: i = (sp-1)->u.number == sp->u.number; break; case T_POINTER: i = (sp-1)->u.vec == sp->u.vec; break; case T_MAPPING: i = (sp-1)->u.map == sp->u.map; break; case T_STRING: i = strcmp((sp-1)->u.string, sp->u.string) == 0; break; case T_OBJECT: i = (sp-1)->u.ob == sp->u.ob; break; case T_FLOAT: i = (sp-1)->u.real == sp->u.real; break; default: i = 0; break; } pop_n_elems(2); push_number(i); } static void f_ne(int num_arg) { int i; if ((sp-1)->type != sp->type) { pop_stack(); assign_svalue(sp, &const1); return; } switch(sp->type) { case T_NUMBER: i = (sp-1)->u.number != sp->u.number; break; case T_STRING: i = strcmp((sp-1)->u.string, sp->u.string); break; case T_POINTER: i = (sp-1)->u.vec != sp->u.vec; break; case T_MAPPING: i = (sp-1)->u.map != sp->u.map; break; case T_OBJECT: i = (sp-1)->u.ob != sp->u.ob; break; case T_FLOAT: i = (sp-1)->u.real != sp->u.real; break; default: fatal("Illegal type to !=\n"); } pop_n_elems(2); push_number(i); } static void f_not(int num_arg) { if (sp->type == T_NUMBER && sp->u.number == 0) sp->u.number = 1; else assign_svalue(sp, &const0); } static void f_compl(int num_arg) { if (sp->type != T_NUMBER) error("Bad argument to ~\n"); sp->u.number = ~ sp->u.number; } static void f_negate(int num_arg) { if (sp->type == T_FLOAT) { sp->u.real = - sp->u.real; return; } if (sp->type != T_NUMBER) error("Bad argument to unary minus\n"); sp->u.number = - sp->u.number; } static void f_inc(int num_arg) { if (sp->type != T_LVALUE) error("Bad argument to ++\n"); if (sp->u.lvalue->type != T_NUMBER) error("++ of non-numeric argument\n"); sp->u.lvalue->u.number++; assign_svalue(sp, sp->u.lvalue); } static void f_dec(int num_arg) { if (sp->type != T_LVALUE) error("Bad argument to --\n"); if (sp->u.lvalue->type != T_NUMBER) error("-- of non-numeric argument\n"); sp->u.lvalue->u.number--; assign_svalue(sp, sp->u.lvalue); } static void f_post_inc(int num_arg) { if (sp->type != T_LVALUE) error("Bad argument to ++\n"); if (sp->u.lvalue->type != T_NUMBER) error("++ of non-numeric argument\n"); sp->u.lvalue->u.number++; assign_svalue(sp, sp->u.lvalue); sp->u.number--; } static void f_post_dec(int num_arg) { if (sp->type != T_LVALUE) error("Bad argument to --\n"); if (sp->u.lvalue->type != T_NUMBER) error("-- of non-numeric argument\n"); sp->u.lvalue->u.number--; assign_svalue(sp, sp->u.lvalue); sp->u.number++; } static void f_call_otherv(int num_arg) { static void f_call_other(); struct vector *argv = sp->u.vec; int i; argv->ref++; pop_stack(); num_arg = argv->size + 2; for(i = 0; i < argv->size; i++) { push_svalue(&argv->item[i]); } free_vector(argv); f_call_other(num_arg); } static void f_call_other(int num_arg) { struct object *ob; struct svalue *arg, tmp; #ifdef COUNT_CALLS num_call_other++; #endif arg = sp - num_arg + 1; if (arg[0].type == T_NUMBER) { if (arg[0].u.number != 0) error("Bad argument 1 to call_other()"); pop_n_elems(num_arg); push_number(0); return; } if (arg[0].type == T_POINTER) { struct vector *w, *v = allocate_array(num_arg - 2); struct object *ob; int i, j; for (i = 0; i < num_arg - 2; i++) assign_svalue_no_free(&v->item[i], &arg[i + 2]); pop_n_elems(num_arg - 2); w = allocate_array(arg[0].u.vec->size); for (i = 0; i < arg[0].u.vec->size; i++) { if (arg[0].u.vec->item[i].type != T_OBJECT && arg[0].u.vec->item[i].type != T_STRING) continue; if (arg[0].u.vec->item[i].type == T_OBJECT) ob = arg[0].u.vec->item[i].u.ob; else ob = find_object(arg[0].u.vec->item[i].u.string); if (!ob || ob->flags & O_DESTRUCTED) continue; for (j = 0; j < v->size; j++) push_svalue(&v->item[j]); #ifdef TRACE_CODE if (TRACEP(TRACE_CALL_OTHER)) { char buff[1024]; sprintf(buff,"%s->%s", ob->name,arg[1].u.string); do_trace("Call other ", buff, "\n"); } #endif if (apply_low(arg[1].u.string, ob, v->size, 1) == 0) continue; /* function not found */ w->item[i] = *(sp--); } pop_n_elems(2); push_vector(w); w->ref--; free_vector(v); return; } if (arg[0].type == T_MAPPING) { struct vector *w, *v = allocate_array(num_arg - 2); struct vector *ix, *o; struct object *ob; int i, j; ix = map_domain(arg[0].u.map); o = map_codomain(arg[0].u.map); for (i = 0; i < num_arg - 2; i++) assign_svalue_no_free(&v->item[i], &arg[i + 2]); pop_n_elems(num_arg - 2); w = allocate_array(o->size); for (i = 0; i < o->size; i++) { if (o->item[i].type != T_OBJECT && o->item[i].type != T_STRING) continue; if (o->item[i].type == T_OBJECT) ob = o->item[i].u.ob; else ob = find_object(o->item[i].u.string); if (!ob || ob->flags & O_DESTRUCTED) continue; for (j = 0; j < v->size; j++) push_svalue(&v->item[j]); #ifdef TRACE_CODE if (TRACEP(TRACE_CALL_OTHER)) { char buff[1024]; sprintf(buff,"%s->%s", ob->name, arg[1].u.string); do_trace("Call other ", buff, "\n"); } #endif if (apply_low(arg[1].u.string, ob, v->size, 1) == 0) continue; /* function not found */ w->item[i] = *(sp--); } pop_n_elems(2); push_mapping(make_mapping(ix,w)); sp->u.map->ref--; /* Adjust ref counter */ free_vector(o); free_vector(ix); free_vector(v); free_vector(w); return; } if (arg[0].type == T_OBJECT) ob = arg[0].u.ob; else { ob = find_object(arg[0].u.string); if (ob == 0) error("call_other() failed\n"); } if (current_object->flags & O_DESTRUCTED) { /* * No external calls may be done when this object is * destructed. */ pop_n_elems(num_arg); push_number(0); return; } /* * Send the remaining arguments to the function. */ #ifdef TRACE_CODE if (TRACEP(TRACE_CALL_OTHER)) { char buff[1024]; sprintf(buff,"%s->%s", ob->name, arg[1].u.string); do_trace("Call other ", buff, "\n"); } #endif if (apply_low(arg[1].u.string, ob, num_arg - 2, 1) == 0) { /* Function not found */ pop_n_elems(2); push_number(0); return; } /* * The result of the function call is on the stack. But, so * is the function name and object that was called. * These have to be removed. */ tmp = *sp--; /* Copy the function call result */ pop_n_elems(2); /* Remove old arguments to call_other */ *++sp = tmp; /* Re-insert function result */ } static void f_object_time(int num_arg) { int i; if (sp->type == T_OBJECT) { i = sp->u.ob->created; pop_stack(); push_number(i); } else assign_svalue(sp, &const0); } static void f_intp(int num_arg) { if (sp->type == T_NUMBER) assign_svalue(sp, &const1); else assign_svalue(sp, &const0); } static void f_stringp(int num_arg) { if (sp->type == T_STRING) assign_svalue(sp, &const1); else assign_svalue(sp, &const0); } static void f_objectp(int num_arg) { if (sp->type == T_OBJECT) assign_svalue(sp, &const1); else assign_svalue(sp, &const0); } static void f_pointerp(int num_arg) { if (sp->type == T_POINTER) assign_svalue(sp, &const1); else assign_svalue(sp, &const0); } static void f_mappingp(int num_arg) { if (sp->type == T_MAPPING) assign_svalue(sp, &const1); else assign_svalue(sp, &const0); } static void f_floatp(int num_arg) { if (sp->type == T_FLOAT) assign_svalue(sp, &const1); else assign_svalue(sp, &const0); } static void f_extract(int num_arg) { int len, from, to; struct svalue *arg; char *res; arg = sp - num_arg + 1; len = strlen(arg[0].u.string); if (num_arg == 1) return; /* Simply return argument */ from = arg[1].u.number; if (from < 0) from = len + from; if (from >= len) { pop_n_elems(num_arg); push_string("", STRING_CONSTANT); return; } if (num_arg == 2) { res = string_copy(arg->u.string + from); pop_n_elems(2); push_malloced_string(res); return; } if (arg[2].type != T_NUMBER) error("Bad third argument to extract()\n"); to = arg[2].u.number; if (to < 0) to = len + to; if (to < from) { pop_n_elems(3); push_string("", STRING_CONSTANT); return; } if (to >= len) to = len-1; if (to == len-1) { res = string_copy(arg->u.string + from); pop_n_elems(3); push_malloced_string(res); return; } res = xalloc(to - from + 2); strncpy(res, arg[0].u.string + from, to - from + 1); res[to - from + 1] = '\0'; pop_n_elems(3); push_malloced_string(res); } static void f_range(int num_arg) { if (sp[-1].type != T_NUMBER) error("Bad type of start interval to [ .. ] range.\n"); if (sp[0].type != T_NUMBER) error("Bad type of end interval to [ .. ] range.\n"); if (sp[-2].type == T_POINTER) { struct vector *v; v = slice_array(sp[-2].u.vec, sp[-1].u.number, sp[0].u.number); pop_n_elems(3); if (v) { push_vector(v); v->ref--; /* Will make ref count == 1 */ } else { push_number(0); } } else if (sp[-2].type == T_STRING) { int len, from, to; char *res; len = strlen(sp[-2].u.string); from = sp[-1].u.number; if (from < 0) from = len + from; if (from < 0) from = 0; if (from >= len) { pop_n_elems(3); push_string("", STRING_CONSTANT); return; } to = sp[0].u.number; if (to < 0) to = len + to; if (to < from) { pop_n_elems(3); push_string("", STRING_CONSTANT); return; } if (to >= len) to = len - 1; if (to == len - 1) { res = string_copy(sp[-2].u.string + from); pop_n_elems(3); push_malloced_string(res); return; } res = xalloc(to - from + 2); strncpy(res, sp[-2].u.string + from, to - from + 1); res[to - from + 1] = '\0'; pop_n_elems(3); push_malloced_string(res); } else { error("Bad argument to [ .. ] range operand.\n"); } } static void f_query_verb(int num_arg) { if (last_verb == 0) { push_number(0); return; } push_string(last_verb, STRING_MALLOC); } static void f_exec(int num_arg) { int i; if ((sp-1)->type == T_NUMBER) i = replace_interactive(0, sp->u.ob, current_prog->name); else i = replace_interactive((sp-1)->u.ob, sp->u.ob, current_prog->name); pop_stack(); pop_stack(); push_number(i); } static void f_file_name(int num_arg) { char *name,*res; /* This function now returns a leading '/', except when -o flag */ name = sp->u.ob->name; res = add_slash(name); pop_stack(); push_malloced_string(res); } static void f_users(int num_arg) { struct svalue *ret; if (current_object != master_ob) { push_object(current_object); ret = apply_master_ob(M_VALID_USERS, 1); if (ret && ret->u.number == 0) { push_number(0); return; } } push_vector(users()); /* users() has already set ref count to 1 */ sp->u.vec->ref--; } static void f_set_alarm(int num_arg) { struct svalue *arg = sp - num_arg + 1; int delay, reload, ret; struct vector *v; if (arg[2].type != T_STRING) error("Wrong argument 3 to set_alarm.\n"); if (*(arg[2].u.string) == '.') { pop_n_elems(num_arg); push_number(0); return; } v = allocate_array(num_arg - 3); memcpy(&v->item[0], &arg[3], (num_arg - 3) * sizeof(struct svalue)); sp = &arg[2]; if (!(current_object->flags & O_DESTRUCTED) && search_for_function(arg[2].u.string, current_object->prog)) { if (function_type_mod_found & TYPE_MOD_PRIVATE && inh_offset < function_inherit_found - function_prog_found->num_inherited + 1) error("Atempted callout of private function.\n"); delay = (int) (arg[0].u.real * TIME_RES + 0.5); reload = (int) (arg[1].u.real * TIME_RES + 0.5); ret = new_call_out(current_object, function_index_found, function_inherit_found, delay, reload, v); } free_vector(v); pop_n_elems(3); push_number(ret); } static void f_set_alarmv(int num_arg) { struct svalue *arg = sp - 3; int delay, reload, ret; struct vector *v; if (arg[2].type != T_STRING) error("Wrong argument 3 to set_alarm.\n"); if (arg[3].type != T_POINTER) error("Wrong argument 4 to set_alarm.\n"); if (*(arg[2].u.string) == '.') { pop_n_elems(num_arg); push_number(0); return; } v = sp->u.vec; sp--; if (!(current_object->flags & O_DESTRUCTED) && search_for_function(arg[2].u.string, current_object->prog)) { if (function_type_mod_found & TYPE_MOD_PRIVATE && inh_offset < function_inherit_found - function_prog_found->num_inherited + 1) error("Atempted callout of private function.\n"); delay = (int) (arg[0].u.real * TIME_RES + 0.5); reload = (int) (arg[1].u.real * TIME_RES + 0.5); ret = new_call_out(current_object, function_index_found, function_inherit_found, delay, reload, v); } free_vector(v); pop_n_elems(3); push_number(ret); } static void f_call_out(int num_arg) { struct svalue *arg = sp - num_arg + 1; int delay = 0, ret = 0, reload = 0; struct vector *v = NULL; if (arg[1].type == T_FLOAT) { delay = (int) (arg[1].u.real * TIME_RES + (arg[1].u.real < 0.0 ? -0.5: 0.5)); if (delay == 0 && arg[1].u.real < 0.0) reload = 1; } else { delay = arg[1].u.number * TIME_RES; } if (delay < 0) reload = delay = -delay; if (*(arg[0].u.string) == '.') { pop_n_elems(num_arg); push_number(0); return; } if (num_arg == 3) { v = allocate_array(1); assign_svalue_no_free(&v->item[0], sp); } if (!(current_object->flags & O_DESTRUCTED)) if (search_for_function(arg[0].u.string, current_object->prog)) { if (function_type_mod_found & TYPE_MOD_PRIVATE && inh_offset < function_inherit_found - function_prog_found->num_inherited + 1) error("Atempted callout of private function.\n"); ret = new_call_out(current_object, function_index_found, function_inherit_found, delay, reload, v); } if (v) free_vector(v); pop_n_elems(num_arg); push_number(ret); } static void f_remove_alarm(int num_arg) { extern void delete_call(struct object *, int); delete_call(current_object, sp->u.number); } static void f_get_all_alarms(int num_arg) { struct vector *ret; extern struct vector *get_calls(struct object *); ret = get_calls(current_object); if (ret) { push_vector(ret); ret->ref--; } else push_number(0); } static void f_get_alarm(int num_arg) { struct vector *ret; extern struct vector *get_call(struct object *, int); ret = get_call(current_object, sp->u.number); pop_stack(); if (ret) { push_vector(ret); ret->ref--; } else push_number(0); } #ifdef WORD_WRAP static void f_set_screen_width(int num_arg) { if (! current_object->interactive) return; if (sp->u.number < 0 || sp->u.number == 1) error("Nonsensical screen width\n"); current_object->interactive->screen_width = sp->u.number; if (current_object->interactive->current_column >= sp->u.number) current_object->interactive->current_column = sp->u.number - 1; /* Return first argument */ } static void f_query_screen_width(int num_arg) { int i; i = -1; if (current_object->interactive) i = current_object->interactive->screen_width; push_number(i); } #endif static void f_sprintf(int num_arg) { char *s; /* * string_print_formatted() returns a pointer to it's internal * buffer, or to an internal constant... Either way, it must * be copied before it's returned as a string. */ s = string_print_formatted(1, (sp-num_arg+1)->u.string, num_arg-1, sp-num_arg+2); pop_n_elems(num_arg); if (!s) push_number(0); else push_malloced_string(string_copy(s)); } static void f_member_array(int num_arg) { struct vector *v; int i; if (sp->type == T_NUMBER) { pop_n_elems(2); push_number(-1); return; } v = sp->u.vec; check_for_destr(sp); for (i=0; i < v->size; i++) { if (v->item[i].type != (sp-1)->type) continue; switch((sp-1)->type) { case T_STRING: if (strcmp((sp-1)->u.string, v->item[i].u.string) == 0) break; continue; case T_POINTER: if ((sp-1)->u.vec == v->item[i].u.vec) break; continue; case T_MAPPING: if ((sp-1)->u.map == v->item[i].u.map) break; continue; case T_OBJECT: if ((sp-1)->u.ob == v->item[i].u.ob) break; continue; case T_NUMBER: if ((sp-1)->u.number == v->item[i].u.number) break; continue; default: fatal("Bad type to member_array(): %d\n", (sp-1)->type); } break; } if (i == v->size) i = -1; /* Return -1 for failure */ pop_n_elems(2); push_number(i); } static void f_move_object(int num_arg) { struct object *ob; if (sp->type == T_OBJECT) ob = sp->u.ob; else ob = find_object(sp->u.string); move_object(ob); } static void f_update_actions(int num_arg) { update_actions(current_object); } static void f_function_exists(int num_arg) { char *str, *res; str = function_exists((sp-1)->u.string, sp->u.ob); pop_n_elems(2); if (str) { res = add_slash(str); if (str = strrchr(res, '.')) *str = 0; push_malloced_string(res); } else { push_number(0); } } static void f_snoop(int num_arg) { struct object *ob; /* * This one takes a variable number of arguments. It returns * 0 or an object. */ if (!command_giver) { pop_n_elems(num_arg); push_number(0); } else { ob = 0; /* Do not remove this, it is not 0 by default */ switch (num_arg) { case 1: if (set_snoop(sp->u.ob, 0)) ob = sp->u.ob; break; case 2: if (set_snoop((sp-1)->u.ob, sp->u.ob)) ob = sp->u.ob; break; default: ob = 0; break; } pop_n_elems(num_arg); if (ob) push_object(ob); else push_number(0); } } static void f_add_action(int num_arg) { struct svalue *arg = sp - num_arg + 1; if (num_arg == 3) { if (arg[2].type != T_NUMBER) bad_arg(3, F_ADD_ACTION, &arg[2]); } add_action(&arg[0], num_arg > 1 ? &arg[1] : (struct svalue *)0, num_arg > 2 ? arg[2].u.number : 0); pop_n_elems(num_arg - 1); } static void f_allocate(int num_arg) { struct vector *v; v = allocate_array(sp->u.number); /* Will have ref count == 1 */ pop_stack(); push_vector(v); v->ref--; } static void f_ed(int num_arg) { if (num_arg == 0) { push_number(0); } else if (num_arg == 1) { ed_start(sp->u.string, 0, 0); } else { if (sp->type == T_STRING) ed_start((sp-1)->u.string, sp->u.string, current_object); else ed_start((sp-1)->u.string, 0 , 0); pop_stack(); } } static void f_crypt(int num_arg) { char salt[2]; char *res; char *choise = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789./"; if (sp->type == T_STRING && strlen(sp->u.string) >= 2) { salt[0] = sp->u.string[0]; salt[1] = sp->u.string[1]; } else { salt[0] = choise[random_number(strlen(choise), 0)]; salt[1] = choise[random_number(strlen(choise), 0)]; } #ifdef sun res = string_copy(_crypt((sp-1)->u.string, salt)); #else res = string_copy(crypt((sp-1)->u.string, salt)); #endif pop_n_elems(2); push_malloced_string(res); } static void f_destruct(int num_arg) { destruct_object(current_object); push_number(0); } static void f_random(int num_arg) { int seed; struct svalue *arg = sp - num_arg + 1; if (num_arg > 1) { seed = arg[1].u.number; pop_stack(); sp->u.number = random_number(arg[0].u.number, seed); } else sp->u.number = random_number(arg[0].u.number, 0); } static void f_while(int num_arg) { fatal("F_WHILE should not appear.\n"); } static void f_do(int num_arg) { fatal("F_DO should not appear.\n"); } static void f_for(int num_arg) { fatal("F_FOR should not appear.\n"); } static unsigned short read_short(char *addr) { unsigned short ret; ((char *)&ret)[0] = ((char *)addr)[0]; ((char *)&ret)[1] = ((char *)addr)[1]; return ret; } static int read_int(char *addr) { int ret; ((char *)&ret)[0] = ((char *)addr)[0]; ((char *)&ret)[1] = ((char *)addr)[1]; ((char *)&ret)[2] = ((char *)addr)[2]; ((char *)&ret)[3] = ((char *)addr)[3]; return ret; } static int cmp_values(long val, long cmp, int how) { if (how) return strcmp((char *)val, current_prog->rodata + cmp); else return val - cmp; } struct case_entry { int value; unsigned short offset; }; static void f_switch(int num_arg) { #define TABLE_OFF 1 #define TABLE_END_OFF 4 #define DEFAULT_OFF 6 #define STR_TABLE 1 #define E_VALUE 0 #define E_OFFSET 4 #define ENTRY_SIZE 6 #define RANGE_OFFSET ((unsigned short)-1) int tab_head, tab_tail, tab_mid; unsigned int tab_start, tab_end, offset; long search_val; short tab_type, is_str; tab_type = (*pc) & 0xff; is_str = tab_type & STR_TABLE; tab_start = read_short(pc + TABLE_OFF); tab_end = read_short(pc + TABLE_END_OFF); tab_head = 0; tab_tail = (tab_end - tab_start) / ENTRY_SIZE - 1; if (is_str) { /* This table has 0 as case label as first entry in the table */ if (sp->type == T_NUMBER && !sp->u.number) { pc = current_prog->program + read_short(current_prog->program + tab_start + E_OFFSET); pop_stack(); return; } if (sp->type != T_STRING) bad_arg(1, F_SWITCH, sp); search_val = (long) sp->u.string; tab_head++; } else if (sp->type == T_NUMBER) search_val = sp->u.number; else bad_arg(1, F_SWITCH, sp); while (tab_head <= tab_tail) { tab_mid = (tab_head + tab_tail) / 2; if (read_short(current_prog->program + tab_start + tab_mid * ENTRY_SIZE + E_OFFSET) == RANGE_OFFSET || tab_mid != tab_head && read_short(current_prog->program + tab_start + tab_mid * ENTRY_SIZE + E_OFFSET - ENTRY_SIZE) == RANGE_OFFSET && (tab_mid--, 1)) { /* It is a range entry */ int lo_value, hi_value; lo_value = read_int(current_prog->program + tab_start + tab_mid * ENTRY_SIZE + E_VALUE); hi_value = read_int(current_prog->program + tab_start + tab_mid * ENTRY_SIZE + E_VALUE + ENTRY_SIZE); if (cmp_values(search_val, lo_value, is_str) < 0) tab_tail = tab_mid - 1; else if (cmp_values(search_val, hi_value, is_str) > 0) tab_head = tab_mid + 2; else { pc = current_prog->program + read_short(current_prog->program + tab_start + tab_mid * ENTRY_SIZE + E_OFFSET + ENTRY_SIZE); pop_stack(); return; } } else { /* It is an ordinary entry */ int value, cmp; value = read_int(current_prog->program + tab_start + tab_mid * ENTRY_SIZE + E_VALUE); if ((cmp = cmp_values(search_val, value, is_str)) == 0) { pc = current_prog->program + read_short(current_prog->program + tab_start + tab_mid * ENTRY_SIZE + E_OFFSET); pop_stack(); return; } else if (cmp < 0) tab_tail = tab_mid - 1; else tab_head = tab_mid + 1; } } /* No match, use default */ pc = current_prog->program + read_short(pc + DEFAULT_OFF); pop_stack(); return; } static void f_break(int num_arg) { error("Bad break code, this should not happend.\n"); } static void f_subscript(int num_arg) { fatal("F_SUBSCRIPT should not appear.\n"); } static void f_strlen(int num_arg) { int i; if (sp->type == T_NUMBER) i = 0; else i = strlen(sp->u.string); pop_stack(); push_number(i); } static void f_mkmapping(int num_arg) { struct mapping *m; if ((sp-1)->type == T_POINTER && sp->type == T_POINTER) m = make_mapping((sp-1)->u.vec, sp->u.vec); else if ((sp-1)->type == T_NUMBER && sp->type == T_POINTER) m = make_mapping(NULL, sp->u.vec); else if ((sp-1)->type == T_POINTER && sp->type == T_NUMBER) m = make_mapping((sp-1)->u.vec, NULL); else if ((sp-1)->type == T_NUMBER && sp->type == T_NUMBER) { error("One argument must be a pointer."); return; } pop_n_elems(2); push_mapping(m); m->ref--; /* Will make ref count == 1 */ } static void f_m_sizeof(int num_arg) { int i; if (sp->type == T_MAPPING) i = card_mapping(sp->u.map); else i = 0; pop_stack(); push_number(i); } static void f_m_indexes(int num_arg) { struct vector *v; if (sp->type == T_MAPPING) { v = map_domain(sp->u.map); pop_stack(); push_vector(v); v->ref--; /* Will make ref count == 1 */ } else { pop_stack(); push_number(0); } } static void f_m_values(int num_arg) { struct vector *v; if (sp->type == T_MAPPING) { v = map_codomain(sp->u.map); pop_stack(); push_vector(v); v->ref--; /* Will make ref count == 1 */ } else { pop_stack(); push_number(0); } } static void f_m_delete(int num_arg) { struct mapping *m; if ((sp-1)->type == T_MAPPING) { m = remove_mapping((sp-1)->u.map, sp); pop_n_elems(2); push_mapping(m); m->ref--; /* Refcount is 2 after the push */ } else { pop_n_elems(2); push_number(0); } } static void f_sizeof(int num_arg) { int i; if (sp->type == T_NUMBER) i = 0; else i = sp->u.vec->size; pop_stack(); push_number(i); } static void f_lower_case(int num_arg) { char *str; int i; if (sp->type == T_NUMBER) return; str = string_copy(sp->u.string); for (i = strlen(str)-1; i>=0; i--) if (isalpha(str[i])) str[i] |= 'a' - 'A'; pop_stack(); push_malloced_string(str); } static void f_readable_string(int num_arg) { char *str; int i; if (sp->type == T_NUMBER) return; str = string_copy(sp->u.string); for (i = strlen(str)-1; i>=0; i--) if (str[i] < ' ' || !isprint(str[i])) str[i] = '.'; pop_stack(); push_malloced_string(str); } static void f_capitalize(int num_arg) { if (sp->type == T_NUMBER) return; if (islower(sp->u.string[0])) { char *str; str = string_copy(sp->u.string); str[0] += 'A' - 'a'; pop_stack(); push_malloced_string(str); } } static void f_process_string(int num_arg) { extern char *process_string (char *, int); char *str; str = process_string(sp[-1].u.string, sp->u.number); pop_stack(); if (str != sp->u.string) { pop_stack(); push_malloced_string(str); } } static void f_process_value(int num_arg) { extern struct svalue *process_value (char *, int); struct svalue *ret; ret = process_value(sp[-1].u.string, sp->u.number); pop_stack(); pop_stack(); if (ret) { push_svalue(ret); } else push_number(0); } static void f_command(int num_arg) { int i; i = command_for_object(sp->u.string, 0); pop_stack(); push_number(i); } static void f_get_dir(int num_arg) { struct vector *v = get_dir(sp->u.string); pop_stack(); if (v) { push_vector(v); v->ref--; /* Will now be 1. */ } else push_number(0); } static void f_rm(int num_arg) { int i; i = remove_file(sp->u.string); pop_stack(); push_number(i); } static void f_mkdir(int num_arg) { char *path; path = check_valid_path(sp->u.string, current_object, "mkdir", 1); /* pop_stack(); see comment above... */ if (path == 0 || mkdir(path, 0774) == -1) assign_svalue(sp, &const0); else assign_svalue(sp, &const1); } static void f_rmdir(int num_arg) { char *path; path = check_valid_path(sp->u.string, current_object, "rmdir", 1); /* pop_stack(); rw - what the heck ??? */ if (path == 0 || rmdir(path) == -1) assign_svalue(sp, &const0); else assign_svalue(sp, &const1); } static void f_input_to(int num_arg) { struct svalue *arg = sp - num_arg + 1; struct vector *v; int flag = 1, i; v = allocate_array((num_arg > 1) ? num_arg - 2:0); if (num_arg == 1 || (arg[1].type == T_NUMBER && arg[1].u.number == 0)) flag = 0; for (i = 2; i < num_arg; i++) assign_svalue_no_free(&(v->item[i - 2]), &(arg[i])); i = input_to(arg[0].u.string, flag,v); pop_n_elems(num_arg); push_number(i); } static void f_set_living_name(int num_arg) { set_living_name(current_object, sp->u.string); } static void f_parse_command(int num_arg) { struct svalue *arg; int i; num_arg = EXTRACT_UCHAR(pc); pc++; arg = sp - num_arg + 1; if (arg[0].type != T_STRING) bad_arg(1, F_PARSE_COMMAND, &arg[0]); if (arg[1].type != T_OBJECT && arg[1].type != T_POINTER) bad_arg(2, F_PARSE_COMMAND, &arg[1]); if (arg[2].type != T_STRING) bad_arg(3, F_PARSE_COMMAND, &arg[2]); if (arg[1].type == T_POINTER) check_for_destr(&arg[1]); i = parse(arg[0].u.string, &arg[1], arg[2].u.string, &arg[3], num_arg-3); pop_n_elems(num_arg); /* Get rid of all arguments */ push_number(i); /* Push the result value */ } static void f_debug(int num_arg) { struct svalue *arg, *ret; int i; arg = sp - num_arg + 1; if (current_object != master_ob) { push_object(current_object); for (i = 0; i < num_arg; i++) push_svalue(&arg[i]); ret = apply_master_ob(M_VALID_DEBUG, num_arg + 1); if (!ret || ret->u.number == 0) { pop_n_elems(num_arg); /* Get rid of all arguments */ push_number(0); } } arg = sp - num_arg + 1; if (arg[0].type != T_STRING) bad_arg(1, F_DEBUG, &arg[0]); ret = debug_command(arg[0].u.string, num_arg-1, sp-num_arg+2); pop_n_elems(num_arg - 1); /* Get rid of all arguments */ assign_svalue(sp, ret); free_svalue(ret); } static void f_sscanf(int num_arg) { int i; num_arg = EXTRACT_UCHAR(pc); pc++; i = inter_sscanf(num_arg); pop_n_elems(num_arg); push_number(i); } static void f_enable_commands(int num_arg) { enable_commands(1); push_number(1); } static void f_disable_commands(int num_arg) { enable_commands(0); push_number(0); } static void f_present(int num_arg) { struct object *ob; ob = object_present((sp-1), sp); pop_stack(); pop_stack(); if (ob) push_object(ob); else push_number(0); } static void f_const0(int num_arg) { push_number(0); } static void f_const1(int num_arg) { push_number(1); } static void f_number(int num_arg) { int i; ((char *)&i)[0] = pc[0]; ((char *)&i)[1] = pc[1]; ((char *)&i)[2] = pc[2]; ((char *)&i)[3] = pc[3]; pc += 4; push_number(i); } static void f_assign(int num_arg) { #ifdef DEBUG if (sp[-1].type != T_LVALUE) fatal("Bad argument to F_ASSIGN\n"); #endif assign_svalue((sp-1)->u.lvalue, sp); assign_svalue(sp-1, sp); pop_stack(); } static void f_ctime(int num_arg) { char *cp; cp = string_copy(time_string(sp->u.number)); pop_stack(); push_malloced_string(cp); /* Now strip the newline. */ cp = strchr(cp, '\n'); if (cp) *cp = '\0'; } static void f_add_eq(int num_arg) { struct svalue *argp; char *new_str; int i; float fl; if (sp[-1].type != T_LVALUE) bad_arg(1, F_ADD_EQ, sp-1); argp = sp[-1].u.lvalue; switch(argp->type) { case T_STRING: if (sp->type == T_STRING) { int l = strlen(argp->u.string); new_str = xalloc(l + strlen(sp->u.string) + 1); strcpy(new_str, argp->u.string); strcpy(new_str+l, sp->u.string); pop_n_elems(2); push_malloced_string(new_str); } else if (sp->type == T_NUMBER) { char buff[20]; sprintf(buff, "%d", sp->u.number); new_str = xalloc(strlen(argp->u.string) + strlen(buff) + 1); strcpy(new_str, argp->u.string); strcat(new_str, buff); pop_n_elems(2); push_malloced_string(new_str); } else { bad_arg(2, F_ADD_EQ, sp); } break; case T_NUMBER: if (sp->type == T_NUMBER) { i = argp->u.number + sp->u.number; pop_n_elems(2); push_number(i); } else { error("Bad type number to rhs +=.\n"); } break; case T_FLOAT: if (sp->type == T_FLOAT) { fl = argp->u.real + sp->u.real; pop_n_elems(2); push_float(fl); } else { error("Bad type number to rhs +=.\n"); } break; case T_MAPPING: if (sp->type != T_MAPPING) { error("Bad type to rhs +=.\n"); } else { struct mapping *m; check_for_destr(argp); check_for_destr(sp); addto_mapping(argp->u.map, sp->u.map); m = argp->u.map; m->ref++; pop_n_elems(2); push_mapping(m); m->ref--; /* Fix ref count */ } break; case T_POINTER: if (sp->type != T_POINTER) { error("Bad type to rhs +=.\n"); } else { struct vector *v; check_for_destr(argp); check_for_destr(sp); v = add_array(argp->u.vec,sp->u.vec); pop_n_elems(2); push_vector(v); /* This will make ref count == 2 */ v->ref--; } break; default: error("Bad type to lhs += "); } assign_svalue(argp, sp); } static void f_sub_eq(int num_arg) { struct svalue *argp; if (sp[-1].type != T_LVALUE) bad_arg(1, F_SUB_EQ, sp-1); argp = sp[-1].u.lvalue; switch (argp->type) { case T_NUMBER: if (sp->type != T_NUMBER) error("Bad right type to -="); argp->u.number -= sp->u.number; sp--; break; case T_FLOAT: if (sp->type != T_FLOAT) error("Bad right type to -="); argp->u.real -= sp->u.real; sp--; break; case T_POINTER: { struct vector *v; if (sp->type != T_POINTER) error("Bad right type to -="); v = subtract_array(argp->u.vec, sp->u.vec); pop_stack(); pop_stack(); if (v == 0) { push_number(0); } else { push_vector(v); /* This will make ref count == 2 */ v->ref--; } assign_svalue(argp, sp); break; } default: error("Bad left type to -=.\n"); } assign_svalue(sp, argp); } static void f_mult_eq(int num_arg) { struct svalue *argp; float fl; int i; if (sp[-1].type != T_LVALUE) bad_arg(1, F_MULT_EQ, sp-1); argp = sp[-1].u.lvalue; if (argp->type == T_FLOAT && sp->type == T_FLOAT) { fl =argp->u.real * sp->u.real; pop_n_elems(2); push_float(fl); assign_svalue(argp, sp); return; } if (argp->type != T_NUMBER) error("Bad left type to *=.\n"); if (sp->type != T_NUMBER) error("Bad right type to *="); i = argp->u.number * sp->u.number; pop_n_elems(2); push_number(i); assign_svalue(argp, sp); } static void f_and_eq(int num_arg) { struct svalue *argp; int i; if (sp[-1].type != T_LVALUE) bad_arg(1, F_AND_EQ, sp-1); argp = sp[-1].u.lvalue; switch (argp->type) { case T_NUMBER: if (sp->type != T_NUMBER) error("Bad right type to &="); i = argp->u.number & sp->u.number; pop_n_elems(2); push_number(i); assign_svalue(argp, sp); break; case T_POINTER: { struct vector *v; if (sp->type != T_POINTER) error("Bad right type to &="); v = intersect_array(argp->u.vec, sp->u.vec); pop_stack(); pop_stack(); if (v == 0) { push_number(0); } else { push_vector(v); /* This will make ref count == 2 */ v->ref--; } assign_svalue(argp, sp); break; } default: error("Bad left type to &=.\n"); } } static void f_or_eq(int num_arg) { struct svalue *argp; int i; if (sp[-1].type != T_LVALUE) bad_arg(1, F_OR_EQ, sp-1); argp = sp[-1].u.lvalue; if (argp->type != T_NUMBER) error("Bad left type to |=.\n"); if (sp->type != T_NUMBER) error("Bad right type to |="); i = argp->u.number | sp->u.number; pop_n_elems(2); push_number(i); assign_svalue(argp, sp); } static void f_xor_eq(int num_arg) { struct svalue *argp; int i; if (sp[-1].type != T_LVALUE) bad_arg(1, F_XOR_EQ, sp-1); argp = sp[-1].u.lvalue; if (argp->type != T_NUMBER) error("Bad left type to ^=.\n"); if (sp->type != T_NUMBER) error("Bad right type to ^="); i = argp->u.number ^ sp->u.number; pop_n_elems(2); push_number(i); assign_svalue(argp, sp); } static void f_lsh_eq(int num_arg) { struct svalue *argp; int i; if (sp[-1].type != T_LVALUE) bad_arg(1, F_LSH_EQ, sp-1); argp = sp[-1].u.lvalue; if (argp->type != T_NUMBER) error("Bad left type to <<=.\n"); if (sp->type != T_NUMBER) error("Bad right type to <<="); i = argp->u.number << sp->u.number; pop_n_elems(2); push_number(i); assign_svalue(argp, sp); } static void f_rsh_eq(int num_arg) { struct svalue *argp; int i; if (sp[-1].type != T_LVALUE) bad_arg(1, F_RSH_EQ, sp-1); argp = sp[-1].u.lvalue; if (argp->type != T_NUMBER) error("Bad left type to >>=.\n"); if (sp->type != T_NUMBER) error("Bad right type to >>="); i = argp->u.number >> sp->u.number; pop_n_elems(2); push_number(i); assign_svalue(argp, sp); } #ifdef F_COMBINE_FREE_LIST static void f_combine_free_list(int num_arg) { #ifdef MALLOC_malloc push_number(resort_free_list()); #else push_number(0); #endif } #endif static void f_div_eq(int num_arg) { struct svalue *argp; int i; float fl; if (sp[-1].type != T_LVALUE) bad_arg(1, F_DIV_EQ, sp-1); argp = sp[-1].u.lvalue; if (argp->type == T_FLOAT && sp->type == T_FLOAT) { if (sp->u.real == 0.0) error("Division by 0\n"); fl = argp->u.real / sp->u.real; pop_n_elems(2); push_float(fl); assign_svalue(argp, sp); return; } if (argp->type != T_NUMBER) error("Bad left type to /=.\n"); if (sp->type != T_NUMBER) error("Bad right type to /="); if (sp->u.number == 0) error("Division by 0\n"); i = argp->u.number / sp->u.number; pop_n_elems(2); push_number(i); assign_svalue(argp, sp); } static void f_mod_eq(int num_arg) { struct svalue *argp; int i; if (sp[-1].type != T_LVALUE) bad_arg(1, F_MOD_EQ, sp-1); argp = sp[-1].u.lvalue; if (argp->type != T_NUMBER) error("Bad left type to %=.\n"); if (sp->type != T_NUMBER) error("Bad right type to %="); if (sp->u.number == 0) error("Division by 0\n"); i = argp->u.number % sp->u.number; pop_n_elems(2); push_number(i); assign_svalue(argp, sp); } static void f_string(int num_arg) { unsigned short string_number; ((char *)&string_number)[0] = pc[0]; ((char *)&string_number)[1] = pc[1]; pc += 2; push_string(current_prog->rodata + string_number, STRING_MALLOC); } static void f_cindent(int num_arg) { char *path; path = check_valid_path(sp->u.string, current_object, "cindent", 1); if (path) { if (indent_program(path)) { assign_svalue(sp, &const1); return; } } else { add_message("Illegal attempt to indent\n"); } assign_svalue(sp, &const0); } static void f_unique_array(int num_arg) { struct vector *res; if ((sp - (num_arg - 1))->type == T_NUMBER) { pop_n_elems(num_arg); push_number(0); return; } if (num_arg < 3) { check_for_destr(sp-1); res = make_unique((sp-1)->u.vec, sp->u.string, &const0); } else { check_for_destr(sp-2); res = make_unique((sp-2)->u.vec, (sp-1)->u.string, sp); pop_stack (); } pop_n_elems(2); if (res) { push_vector (res); /* This will make ref count == 2 */ res->ref--; } else push_number (0); } static void f_rename(int num_arg) { int i; i = do_rename((sp-1)->u.string, sp->u.string); pop_n_elems(2); push_number(i); } static void f_map(int num_arg) { struct vector *res; struct svalue *arg; struct mapping *m; struct object *ob; arg = sp - num_arg + 1; ob = 0; if (arg[2].type == T_OBJECT) ob = arg[2].u.ob; else if (arg[2].type == T_STRING) ob = find_object(arg[2].u.string); if (!ob) bad_arg (3, F_MAP, &arg[2]); if (arg[0].type == T_POINTER) { check_for_destr(&arg[0]); res = map_array(arg[0].u.vec, arg[1].u.string, ob, num_arg > 3 ? sp : (struct svalue *)0); } else res = 0; if (arg[0].type == T_MAPPING) { check_for_destr(&arg[0]); m = map_map(arg[0].u.map, arg[1].u.string, ob, num_arg > 3 ? sp : (struct svalue *)0); } else { m = 0; } pop_n_elems (num_arg); if (res) { push_vector(res); /* This will make ref count == 2 */ res->ref--; } else if (m) { push_mapping(m); /* This will make ref count == 2 */ m->ref--; } else { push_number (0); } } static void f_sqrt(int num_arg) { extern double sqrt(); sp->u.real = sqrt((double)sp->u.real); } #include "efun_table.h" #ifdef USE_SWAP void access_object(struct object *ob) { extern struct object *obj_list; extern struct object *swap_ob; if (ob->flags & O_SWAPPED) load_ob_from_swap(ob); ob->time_of_ref = current_time; if (ob != obj_list && !(ob->flags & O_DESTRUCTED)) { ob->prev_all->next_all = ob->next_all; ob->next_all->prev_all = ob->prev_all; if (ob == swap_ob) swap_ob = ob->prev_all; ob->next_all = obj_list; ob->prev_all = obj_list->prev_all; obj_list->prev_all->next_all = ob; obj_list->prev_all = ob; obj_list = ob; } } void access_program(struct program *prog) { extern struct program *prog_list; extern struct program *swap_prog; if (prog->program == (char *)0) load_prog_from_swap(prog); prog->time_of_ref = current_time; if (prog != prog_list) { prog->prev_all->next_all = prog->next_all; prog->next_all->prev_all = prog->prev_all; if (prog == swap_prog) swap_prog = prog->prev_all; prog->next_all = prog_list; prog->prev_all = prog_list->prev_all; prog_list->prev_all->next_all = prog; prog_list->prev_all = prog; prog_list = prog; } } #endif static void eval_instruction(char *p) { struct object *ob; int i; int instruction; unsigned short ext_instr; #ifdef DEBUG struct svalue *expected_stack; #endif struct svalue *argp; float fl; /* Next F_RETURN at this level will return out of eval_instruction() */ csp->extern_call = 1; #ifdef RUSAGE { #ifdef SOLARIS struct tms buffer; #else struct rusage rus; #endif long cpu; #ifdef SOLARIS if (times(&buffer) != -1) cpu = (buffer.tms_utime + buffer.tms_stime); #else if (getrusage(RUSAGE_SELF, &rus) >= 0) { cpu = rus.ru_utime.tv_sec * 1000 + rus.ru_utime.tv_usec / 1000 + rus.ru_stime.tv_sec * 1000 + rus.ru_stime.tv_usec / 1000; } #endif else cpu = (-1); if (progcpui >= MAX_CPU_STACK) fatal("CPU-Stack overflow.\n"); progcpu[progcpui].prog = current_prog; progcpu[progcpui++].cpu = cpu; } #endif pc = p; again: i = instruction = EXTRACT_UCHAR(pc); #ifdef EXTENDED_INSTRUCTIONS if (instruction == F_EXT - F_OFFSET) { ((char *)&ext_instr)[0] = pc[1]; ((char *)&ext_instr)[1] = pc[2]; instruction = ext_instr; } #endif #ifdef TRACE_CODE previous_instruction[last] = instruction + F_OFFSET; previous_pc[last] = pc; stack_size[last] = sp - fp - csp->num_local_variables; last = (last + 1) % (sizeof previous_instruction / sizeof (int)); #endif #ifdef EXTENDED_INSTRUCTIONS if (i == F_EXT - F_OFFSET) pc += 2; #endif pc++; eval_cost++; if (eval_cost > MAX_COST) { printf("eval_cost too big %d\n", eval_cost); eval_cost = 0; error("Too long evaluation. Execution aborted.\n"); } /* * Execute current instruction. Note that all functions callable * from LPC must return a value. This does not apply to control * instructions, like F_JUMP. */ { int xnum_arg; if (instrs[instruction].min_arg != instrs[instruction].max_arg) { xnum_arg = num_arg = EXTRACT_UCHAR(pc); pc++; } else { xnum_arg = -1; num_arg = instrs[instruction].min_arg; } if (num_arg > 0) { int type1 = (sp-num_arg+1)->type, type2 = (sp-num_arg+2)->type; if (instrs[instruction].type[0] != 0 && (instrs[instruction].type[0] & type1) == 0) { bad_arg(1, instruction + F_OFFSET, sp-num_arg+1); } if (num_arg > 1) { if (instrs[instruction].type[1] != 0 && (instrs[instruction].type[1] & type2) == 0) { bad_arg(2, instruction + F_OFFSET, sp-num_arg+2); } } } /* * Safety measure. It is supposed that the evaluator knows * the number of arguments. */ num_arg = xnum_arg; } #ifdef DEBUG if (num_arg != -1) { expected_stack = sp - num_arg + 1; } else { expected_stack = 0; } #endif instruction += F_OFFSET; #ifdef OPCPROF if (instruction >= 0 && instruction < MAXOPC) opcount[instruction]++; #endif /* * Execute the instructions. The number of arguments are correct, * and the type of the two first arguments are also correct. */ #ifdef TRACE_CODE if (TRACEP(TRACE_EXEC)) { do_trace("Exec ", get_f_name(instruction), "\n"); } #endif switch(instruction) { default: #ifdef DEBUG if (instruction >= EFUN_FIRST && instruction <= EFUN_LAST) #endif efun_table[instruction - EFUN_FIRST](num_arg); #ifdef DEBUG else fatal("Undefined instruction %s (%d)\n", get_f_name(instruction), instruction); #endif break; case F_RETURN: { struct svalue sv; sv = *sp--; /* * Deallocate frame and return. */ for (i=0; i < csp->num_local_variables; i++) pop_stack(); sp++; #ifdef DEBUG if (sp != fp) fatal("Bad stack at F_RETURN\n"); /* marion */ #endif *sp = sv; /* This way, the same ref counts are maintained */ pop_control_stack(); tracedepth--; #ifdef TRACE_CODE if (TRACEP(TRACE_RETURN)) { do_trace("Return", "", ""); if (TRACEHB) { if (TRACETST(TRACE_ARGS)) { write_socket(string_print_formatted(0, " with value: %O", 1, sp), command_giver); } write_socket("\n", command_giver); } } #endif #ifdef RUSAGE { #ifdef SOLARIS struct tms buffer; #else struct rusage rus; #endif long cpu; int i; #ifdef SOLARIS if (times(&buffer) != -1) cpu = (buffer.tms_utime + buffer.tms_stime); #else if (getrusage(RUSAGE_SELF, &rus) >= 0) { cpu = rus.ru_utime.tv_sec * 1000 + rus.ru_utime.tv_usec / 1000 + rus.ru_stime.tv_sec * 1000 + rus.ru_stime.tv_usec / 1000; } #endif else cpu = (-1); if (--progcpui<0) fatal("CPU-Stack underflow.\n"); if (cpu >= 0 && progcpu[progcpui].cpu >=0) { cpu = cpu - progcpu[progcpui].cpu; for(i = progcpui-1; i >= 0; i--) progcpu[i].cpu += cpu; if (progcpu[progcpui].prog) progcpu[progcpui].prog->cpu += cpu; } } #endif if (csp[1].extern_call) /* The control stack was popped just before */ return; } break; case F_CATCH: /* * Catch/Throw - catch errors in system or other peoples routines. */ { extern jmp_buf error_recovery_context; extern int error_recovery_context_exists; extern struct svalue catch_value; unsigned short new_pc_offset; char *old_pc; /* * Compute address of next instruction after the CATCH statement. */ ((char *)&new_pc_offset)[0] = pc[0]; ((char *)&new_pc_offset)[1] = pc[1]; pc += 2; /* * Save some global variables that must be restored separately * after a longjmp. The stack will have to be manually popped all * the way. */ old_pc = pc; pc = current_prog->program + new_pc_offset; /* save with pc == where to continue */ push_pop_error_context (1); pc = old_pc; /* signal catch OK - print no err msg */ error_recovery_context_exists = 2; if (setjmp(error_recovery_context)) { /* * They did a throw() or error. That means that the control * stack must be restored manually here. * Restore the value of expected_stack also. It is always 0 * for catch(). */ #ifdef DEBUG expected_stack = 0; #endif push_pop_error_context (-1); assign_svalue_no_free(++sp, &catch_value); /* If it was eval_cost too big when cant really catch it */ if (eval_cost == 0) eval_cost = MAX_COST; } /* next error will return 1 by default */ assign_svalue(&catch_value, &const1); break; } } #ifdef DEBUG if ((expected_stack && expected_stack != sp) || sp < fp + csp->num_local_variables - 1) { fatal("Bad stack after evaluation. Instruction %d, num arg %d\n", instruction, num_arg); } #endif /* DEBUG */ goto again; } #ifdef GLOBAL_CACHE struct fcache1 { int tp; char *fn; int ff_inh; int ff_ix; }; #endif int s_f_f(char *name, struct program *prog, struct program *sprog, int inherit_offset, int func_index) { int probe = 0, i; struct program *cprog = prog; int type_mod; #ifdef GLOBAL_CACHE static struct fcache1 fc[GLOBAL_CACHE]; int global_hash_val; extern int globcache_hits; extern int globcache_tries; int hash_val; #endif if (!name) return 0; #ifdef GLOBAL_CACHE /* * Are we looking for the same function in the same program again? * This is common for map, filter etc */ globcache_tries++; global_hash_val = (((unsigned int)prog / sizeof(void *)) ^ ((unsigned int)prog >> 16) ^ ((unsigned int)name / sizeof(void *)) ^ ((unsigned int)name >> 16)) & (GLOBAL_CACHE - 1); if (fc[global_hash_val].tp == prog->id_number && fc[global_hash_val].fn == name) { globcache_hits++; #ifdef CACHE_STATS global_first_saves += prog->num_inherited - fc[global_hash_val].ff_inh; #endif function_inherit_found = fc[global_hash_val].ff_inh; function_index_found = fc[global_hash_val].ff_ix; if (function_inherit_found != -1) { int type_mod = prog->inherit[function_inherit_found].type; function_prog_found = prog->inherit[function_inherit_found].prog; access_program(function_prog_found); function_type_mod_found = function_prog_found-> functions[function_index_found].type_flags & TYPE_MOD_MASK ; /* Correct function_type_mod_found */ if (function_type_mod_found & TYPE_MOD_PRIVATE) type_mod &= ~TYPE_MOD_PUBLIC; if (function_type_mod_found & TYPE_MOD_PUBLIC) type_mod &= ~TYPE_MOD_PRIVATE; function_type_mod_found |= type_mod; return 1; } else { function_prog_found = 0; return 0; } } fc[global_hash_val].tp = prog->id_number; fc[global_hash_val].fn = name; fc[global_hash_val].ff_inh = -1; #endif #ifdef CACHE_STATS searches_needed += prog->num_inherited; #endif i = prog->num_inherited - 1; cprog = prog; while (1) { /* Beware of empty function lists */ #ifdef CACHE_STATS searches_done++; #endif if (cprog->num_functions) { /* hash */ probe = PTR_HASH(name, cprog->num_functions); /* Select the right one from the chain */ while (name != cprog->func_hash[probe].name && probe >= 0) probe = cprog->func_hash[probe].next_hashed_function; if (probe >= 0) { probe = cprog->func_hash[probe].func_index; break; } } if (--i < 0) return 0; cprog = prog->inherit[i].prog; } /* Found. Undefined prototypes cannot occur in compiled programs */ #ifdef CACHE_STATS searches_needed -= i; #endif #ifdef GLOBAL_CACHE fc[global_hash_val].ff_inh = #endif function_inherit_found = i; function_prog_found = prog->inherit[i].prog; access_program(function_prog_found); #ifdef GLOBAL_CACHE fc[global_hash_val].ff_ix = #endif function_index_found = probe; function_type_mod_found = prog->inherit[i].prog->functions[probe].type_flags & TYPE_MOD_MASK ; /* Correct function_type_mod_found */ type_mod = prog->inherit[i].type; if (function_type_mod_found & TYPE_MOD_PRIVATE) type_mod &= ~TYPE_MOD_PUBLIC; if (function_type_mod_found & TYPE_MOD_PUBLIC) type_mod &= ~TYPE_MOD_PRIVATE; function_type_mod_found |= type_mod; return 1; } int search_for_function(char *name, struct program *prog) { return s_f_f(findstring(name), prog, NULL, 0, 0); } /* * Apply a fun 'fun' to the program in object 'ob', with * 'num_arg' arguments (already pushed on the stack). * If the function is not found, search in the object pointed to by the * inherit pointer. * If the function name starts with '::', search in the object pointed out * through the inherit pointer by the current object. The 'current_object' * stores the base object, not the object that has the current function being * evaluated. Thus, the variable current_prog will normally be the same as * current_object->prog, but not when executing inherited code. Then, * it will point to the code of the inherited object. As more than one * object can be inherited, the call of function by index number has to * be adjusted. The function number 0 in a superclass object must not remain * number 0 when it is inherited from a subclass object. The same problem * exists for variables. The global variables function_index_offset and * variable_index_offset keep track of how much to adjust the index when * executing code in the superclass objects. * * There is a special case when called from the heart beat, as * current_prog will be 0. When it is 0, set current_prog * to the 'ob->prog' sent as argument. * * Arguments are always removed from the stack. * If the function is not found, return 0 and nothing on the stack. * Otherwise, return 1, and a pushed return value on the stack. * * Note that the object 'ob' can be destructed. This must be handled by * the caller of apply(). * * If the function failed to be called, then arguments must be deallocated * manually ! */ char debug_apply_fun[30]; /* For debugging */ int globcache_tries = 0, globcache_hits = 0, funmap_tries = 0, funmap_hits = 0; static int apply_low(char *fun, struct object *ob, int num_arg, int external) { #ifdef CALL_WARNINGS extern int call_warnings; char *funtmp = fun; #endif char *npc; #ifdef DEBUG struct control_stack *save_csp; #endif struct program *progp; extern int num_error; int ix; short fix; char *sfun; /* * This object will now be used, and is thus a target for * reset later on (when time due). */ #ifdef DEBUG strncpy(debug_apply_fun, fun, sizeof debug_apply_fun); debug_apply_fun[sizeof debug_apply_fun - 1] = '\0'; #endif if (*fun == '.') goto failure; /* * If there is a chain of objects shadowing, start with the first * of these. */ while (ob->shadowed && ob->shadowed != current_object) ob = ob->shadowed; sfun = findstring(fun); retry_for_shadow: progp = ob->prog; #ifdef DEBUG if (ob->flags & O_DESTRUCTED) fatal("apply() on destructed object\n"); #endif if (!(ob->flags & O_CREATED)) create_object(ob); if (ob->flags & O_DESTRUCTED) goto failure; if (s_f_f(sfun, progp, NULL, 0, 0)) { /* Static or private functions may not be called from outside. */ if (((ob != current_object || external) && function_type_mod_found & (TYPE_MOD_STATIC | TYPE_MOD_PRIVATE)) || (function_type_mod_found & TYPE_MOD_PRIVATE && function_prog_found != ob->prog)) ; /* Do nothing */ else { call_function(ob, function_inherit_found, function_index_found, num_arg); return 1; } } /* Not found */ if (ob->shadowing) { /* * This is an object shadowing another. The function was not found, * but can maybe be found in the object we are shadowing. */ ob = ob->shadowing; goto retry_for_shadow; } failure: #ifdef CALL_WARNINGS /* This is code for mudlib debugging purposes. */ if (call_warnings && strcmp(funtmp, ".CTOR") && strcmp(funtmp, "??") && strncmp(funtmp, "add_prop", 8) && strcmp(funtmp, "reset") && strcmp(funtmp, "init_living") && strcmp(funtmp, "replace_soul")&& strcmp(funtmp, "teleledningsanka")) printf("Failed to call %s in %s.\n", funtmp, ob->name); #endif /* Failure. Deallocate stack. */ pop_n_elems(num_arg); return 0; } /* * Arguments are supposed to be * pushed (using push_string() etc) before the call. A pointer to a * 'struct svalue' will be returned. It will be a null pointer if the called * function was not found. Otherwise, it will be a pointer to a static * area in apply(), which will be overwritten by the next call to apply. * Reference counts will be updated for this value, to ensure that no pointers * are deallocated. */ struct svalue * sapply(char *fun, struct object *ob, int num_arg, int ext) { #ifdef DEBUG struct svalue *expected_sp; #endif static struct svalue ret_value = { T_NUMBER }; #ifdef TRACE_CODE if (TRACEP(TRACE_APPLY)) { char buff[1024]; sprintf(buff,"%s->%s", ob->name, fun); do_trace("Apply", "", "\n"); } #endif #ifdef DEBUG expected_sp = sp - num_arg; #endif if (!ob || ob->flags & O_DESTRUCTED) { pop_n_elems(num_arg); return 0; } if (apply_low(fun, ob, num_arg, ext) == 0) return 0; assign_svalue(&ret_value, sp); pop_stack(); #ifdef DEBUG if (expected_sp != sp) fatal("Corrupt stack pointer.\n"); #endif return &ret_value; } struct svalue * apply(char *fun, struct object *ob, int num_arg, int ext) { tracedepth = 0; return sapply(fun, ob, num_arg, ext); } /* * This function is similar to apply(), except that it will not * call the function, only return object name if the function exists, * or 0 otherwise. */ char * function_exists(char *fun, struct object *ob) { struct function *pr; extern char *findstring (char *); #ifdef DEBUG if (ob->flags & O_DESTRUCTED) fatal("function_exists() on destructed object\n"); #endif if (*fun == '.') return 0; if ( search_for_function (fun, ob->prog) && (!(function_type_mod_found & (TYPE_MOD_STATIC|TYPE_MOD_PRIVATE)) || current_object == ob) ) return function_prog_found->name; /* Not found */ return 0; } /* * Call a specific function address in an object. This is done with no * frame set up. It is expected that there are no arguments. Returned * values are removed. */ void call_function(struct object *ob, int inh_index, unsigned int fun, int num_arg) { struct function *funp; struct program *progp; if (inh_index < 0 || fun < 0 || inh_index >= ob->prog->num_inherited || fun >= ob->prog->inherit[inh_index].prog->num_functions) { /* invalid function */ pop_n_elems(num_arg); push_number(0); return; } progp = ob->prog->inherit[inh_index].prog; access_program(progp); funp = &progp->functions[fun]; if (funp->type_flags & NAME_PROTOTYPE) /* Cannot happen. */ return; push_control_stack(funp); csp->ext_call = 1; csp->num_local_variables = num_arg; current_prog = progp; inh_offset = inh_index; previous_ob = current_object; current_object = ob; #ifdef DEBUG if (current_object->prog->inherit[inh_offset].prog != current_prog) fatal("Corrupt inherit offset!\n"); #endif eval_instruction(setup_new_frame(funp)); } /* * Get srccode position including runtime errors in included files. * */ char * inner_get_srccode_position(int offset, char *line_numbers, int lineno_size, char *include_files, char *name) { int inc_file, lineno, tmp_offset, i; unsigned int delta; char *st; static char buff[200]; tmp_offset = 0; lineno = 0; inc_file = 0; for (i = 0; i < lineno_size && tmp_offset < offset; i++) { switch (delta = (unsigned int)line_numbers[i] & 0xff) { case 0xff: { int dl; dl = (unsigned int)line_numbers[++i] & 0xff; lineno += dl; break; } case 0xfe: inc_file = (unsigned int)line_numbers[++i]; lineno = ((unsigned int)line_numbers[++i] & 0xff) << 8; lineno += ((unsigned int)line_numbers[++i] & 0xff); break; default: tmp_offset += delta; lineno++; break; } } if (tmp_offset = offset) lineno++; if (!inc_file) st = name; else { /* * Find the name of the include file. * All includefilenames are stored after eachother on the form: * date:filename<null>date:filename<null> etc */ st = include_files; while (inc_file > 1) { st = &st[strlen(st) + 1]; inc_file--; } st = strchr(st, ':'); st++; } sprintf(buff, "/%s Line: %d", st, lineno); return buff; } char * get_srccode_position(int offset, struct program *progp) { char *ret; if (progp == 0) return ""; #ifdef DEBUG if (offset > progp->program_size) fatal("Illegal offset %d in object %s\n", offset, progp->name); #endif load_lineno_from_swap(progp); ret = inner_get_srccode_position(offset, progp->line_numbers, progp->sizeof_line_numbers, progp->include_files, progp->name); if (progp->swap_lineno_index > 0) swap_lineno(progp); return ret; } char * old_get_srccode_position(int offset, struct program *progp) { int i, top, bot, mid, inc, lin; static char posstr[200]; char *st; if (progp == 0) return ""; load_lineno_from_swap(progp); #ifdef DEBUG if (offset > progp->program_size) fatal("Illegal offset %d in object %s\n", offset, progp->name); #endif #define EOFS(x) progp->line_numbers[x*3] /* The linenumber info is an array with three unsigned shorts / entry: [entry * ix + 0] Offset in bytecode [entry * ix + 1] Include file number (0 means main file) [entry * ix + 2] Line number in above file The entries are sorted in offset order. Below is a simple binary search to find the correct srccode position. */ top = (progp->sizeof_line_numbers / ((sizeof(unsigned short)) * 3)); bot = 0; mid = ((top - bot) >> 1) + bot; while (mid > bot && mid < top) { if (offset > (int)EOFS(mid)) bot = mid; else top = mid; mid = ((top - bot) >> 1) + bot; } inc = progp->line_numbers[mid * 3 + 1]; lin = progp->line_numbers[mid * 3 + 2]; if (!inc) st = progp->name; else { /* Find the name of the include file. All includefilenames are stored after eachother on the form: date:filename<null>date:filename<null> etc */ st = progp->include_files; while (inc > 1) { st = &st[strlen(st) + 1]; inc--; } st = strchr(st, ':'); st++; } sprintf(posstr, "/%s Line: %d", st, lin); if (progp->swap_lineno_index > 0) swap_lineno(progp); return posstr; } /* * Write out a trace. If there is an heart_beat(), then return the * object that had that heart beat. */ char * dump_trace(int how) { struct control_stack *p; char *ret = 0; #ifdef DEBUG int last_instructions (void); #endif char *line; if (current_prog == 0) return 0; if (csp < &control_stack[0]) { (void) printf("No trace.\n"); debug_message("No trace.\n"); return 0; } #if defined(DEBUG) && defined(TRACE_CODE) if (how) (void) last_instructions(); #endif for (p = &control_stack[0]; p < csp; p++) { #define FORM "%-15s in /%s\n /%s\n %s\n" line = get_srccode_position(p[1].pc, p[1].prog); debug_message(FORM, p[0].funp ? p[0].funp->name : "CATCH", p[1].prog->name, p[1].ob->name, line); if (p->funp && strcmp(p->funp->name, "heart_beat") == 0) ret = p->ob?p->ob->name:0; /*crash unliked gc*/ } line = get_srccode_position(pc - current_prog->program, current_prog); debug_message(FORM, p[0].funp ? p[0].funp->name : "CATCH", current_prog->name, current_object->name, line); return ret; } char * get_srccode_position_if_any() { char *ret = ""; if (current_prog) ret = (char *)get_srccode_position(pc - current_prog->program, current_prog); return ret; } static char * find_percent(char *str) { while(1) { str = strchr(str, '%'); if (str == 0) return 0; if (str[1] != '%') return str; str++; } } static int inter_sscanf(int num_arg) { char *fmt; /* Format description */ char *in_string; /* The string to be parsed. */ int number_of_matches; char *cp; struct svalue *arg = sp - num_arg + 1; /* * First get the string to be parsed. */ if (arg[0].type != T_STRING) bad_arg(1, F_SSCANF, &arg[0]); in_string = arg[0].u.string; if (in_string == 0) return 0; /* * Now get the format description. */ if (arg[1].type != T_STRING) bad_arg(2, F_SSCANF, &arg[1]); fmt = arg[1].u.string; /* * First, skip and match leading text. */ for (cp = find_percent(fmt); fmt != cp; fmt++, in_string++) { if (in_string[0] == '\0' || fmt[0] != in_string[0]) return 0; } /* * Loop for every % or substring in the format. Update num_arg and the * arg pointer continuosly. Assigning is done manually, for speed. */ num_arg -= 2; arg += 2; for (number_of_matches = 0; num_arg > 0; number_of_matches++, num_arg--, arg++) { int i, type; if (fmt[0] == '\0') { /* * We have reached end of the format string. * If there are any chars left in the in_string, * then we put them in the last variable (if any). */ if (in_string[0]) { free_svalue(arg->u.lvalue); arg->u.lvalue->type = T_STRING; arg->u.lvalue->u.string = string_copy(in_string); arg->u.lvalue->string_type = STRING_MALLOC; number_of_matches++; } break; } #ifdef DEBUG if (fmt[0] != '%') fatal("Should be a %% now !\n"); #endif type = T_STRING; if (fmt[1] == 'd') type = T_NUMBER; else if (fmt[1] == 'f') type = T_FLOAT; else if (fmt[1] != 's') error("Bad type : '%%%c' in sscanf fmt string.", fmt[1]); fmt += 2; /* * Parsing a number is the easy case. Just use strtol() to * find the end of the number. */ if (type == T_NUMBER) { char *tmp = in_string; int tmp_num; tmp_num = (int) strtol(in_string, &in_string, 10); if(tmp == in_string) { /* No match */ break; } free_svalue(arg->u.lvalue); arg->u.lvalue->type = T_NUMBER; arg->u.lvalue->u.number = tmp_num; while(fmt[0] && fmt[0] == in_string[0]) fmt++, in_string++; if (fmt[0] != '%') { number_of_matches++; break; } continue; } if (type == T_FLOAT) { extern double strtod(); char *tmp = in_string; float tmp_num; tmp_num = (float) strtod(in_string, &in_string); if(tmp == in_string) { /* No match */ break; } free_svalue(arg->u.lvalue); arg->u.lvalue->type = T_FLOAT; arg->u.lvalue->u.real = tmp_num; while(fmt[0] && fmt[0] == in_string[0]) fmt++, in_string++; if (fmt[0] != '%') { number_of_matches++; break; } continue; } /* * Now we have the string case. */ cp = find_percent(fmt); if (cp == fmt) error("Illegal to have 2 adjacent %'s in fmt string in sscanf."); if (cp == 0) cp = fmt + strlen(fmt); /* * First case: There was no extra characters to match. * Then this is the last match. */ if (cp == fmt) { free_svalue(arg->u.lvalue); arg->u.lvalue->type = T_STRING; arg->u.lvalue->u.string = string_copy(in_string); arg->u.lvalue->string_type = STRING_MALLOC; number_of_matches++; break; } for (i = 0; in_string[i]; i++) { if (strncmp(in_string+i, fmt, cp - fmt) == 0) { char *match; /* * Found a match ! */ match = xalloc(i + 1); (void) strncpy(match, in_string, i); in_string += i + cp - fmt; match[i] = '\0'; free_svalue(arg->u.lvalue); arg->u.lvalue->type = T_STRING; arg->u.lvalue->u.string = match; arg->u.lvalue->string_type = STRING_MALLOC; fmt = cp; /* Advance fmt to next % */ break; } } if (fmt == cp) /* If match, then do continue. */ continue; /* * No match was found. Then we stop here, and return * the result so far ! */ break; } return number_of_matches; } /* test stuff ... -- LA */ #ifdef OPCPROF void opcdump() { int i; for(i = 0; i < MAXOPC; i++) if (opcount[i]) printf("%d: %d\n", i, opcount[i]); } #endif /* * Reset the virtual stack machine. */ void reset_machine(int first) { csp = control_stack - 1; if (first) sp = start_of_stack - 1; else pop_n_elems(sp - start_of_stack + 1); } #ifdef TRACE_CODE static char * get_arg(int a, int b) { static char buff[10]; char *from, *to; from = previous_pc[a]; to = previous_pc[b]; if (to - from < 2) return ""; if (to - from == 2) { sprintf(buff, "%d", from[1]); return buff; } if (to - from == 3) { short arg; ((char *)&arg)[0] = from[1]; ((char *)&arg)[1] = from[2]; sprintf(buff, "%d", arg); return buff; } if (to - from == 5) { int arg; ((char *) &arg)[0] = from[1]; ((char *) &arg)[1] = from[2]; ((char *) &arg)[2] = from[3]; ((char *) &arg)[3] = from[4]; sprintf(buff, "%d", arg); return buff; } return ""; } int last_instructions() { int i; i = last; do { if (previous_instruction[i] != 0) printf("%6x: %3d %8s %-25s (%d)\n", previous_pc[i], previous_instruction[i], get_arg(i, (i+1) % (sizeof previous_instruction / sizeof (int))), get_f_name(previous_instruction[i]), stack_size[i] + 1); i = (i + 1) % (sizeof previous_instruction / sizeof (int)); } while (i != last); return last; } #endif /* TRACE_CODE */ #ifdef DEBUG static void count_inherits(struct program *progp, struct program *search_prog) { int i; /* Clones will not add to the ref count of inherited progs */ if (progp->extra_ref != 1) return; for (i = 0; i < progp->num_inherited; i++) { progp->inherit[i].prog->extra_ref++; if (progp->inherit[i].prog == search_prog) printf("Found prog, inherited by %s\n", progp->name); count_inherits(progp->inherit[i].prog, search_prog); } } static void count_ref_in_vector(struct svalue *svp, int num) { struct svalue *p; for (p = svp; p < svp + num; p++) { switch(p->type) { case T_OBJECT: p->u.ob->extra_ref++; continue; case T_POINTER: count_ref_in_vector(&p->u.vec->item[0], p->u.vec->size); p->u.vec->extra_ref++; continue; } } } /* * Clear the extra debug ref count for vectors */ void clear_vector_refs(struct svalue *svp, int num) { struct svalue *p; for (p = svp; p < svp + num; p++) { switch(p->type) { case T_POINTER: clear_vector_refs(&p->u.vec->item[0], p->u.vec->size); p->u.vec->extra_ref = 0; continue; } } } /* * Loop through every object and variable in the game and check * all reference counts. This will surely take some time, and should * only be used for debugging. */ void check_a_lot_ref_counts(struct program *search_prog) { extern struct object *master_ob; struct object *ob; extern struct object *swap_ob; /* * Pass 1: clear the ref counts. */ ob = obj_list; do { ob->extra_ref = 0; ob->prog->extra_ref = 0; if (ob->flags & O_SWAPPED) load_ob_from_swap(ob); clear_vector_refs(ob->variables, ob->prog->num_variables + ob->prog->inherit[ob->prog->num_inherited - 1] .variable_index_offset); ob = ob->next_all; } while (ob != obj_list); swap_ob = obj_list->prev_all; clear_vector_refs(start_of_stack, sp - start_of_stack + 1); /* * Pass 2: Compute the ref counts. */ /* * List of all objects. */ for (ob = obj_list; ob; ob = ob->next_all) { ob->extra_ref++; count_ref_in_vector(ob->variables, ob->prog->num_variables + ob->prog->inherit[ob->prog->num_inherited - 1] .variable_index_offset); ob->prog->extra_ref++; if (ob->prog == search_prog) printf("Found program for object %s\n", ob->name); /* Clones will not add to the ref count of inherited progs */ if (ob->prog->extra_ref == 1) count_inherits(ob->prog, search_prog); } /* * The current stack. */ count_ref_in_vector(start_of_stack, sp - start_of_stack + 1); update_ref_counts_for_players(); count_ref_from_call_outs(); if (master_ob) master_ob->extra_ref++; if (search_prog) return; /* * Pass 3: Check the ref counts. */ for (ob = obj_list; ob; ob = ob->next_all) { if (ob->ref != ob->extra_ref) fatal("Bad ref count in object %s, %d - %d\n", ob->name, ob->ref, ob->extra_ref); if (ob->prog->ref != ob->prog->extra_ref) { check_a_lot_ref_counts(ob->prog); fatal("Bad ref count in prog %s, %d - %d\n", ob->prog->name, ob->prog->ref, ob->prog->extra_ref); } } } #endif /* DEBUG */ /* Generate a debug message to the player */ static void do_trace(char *msg, char *fname, char *post) { char buf[10000]; char *objname; if (!TRACEHB) return; objname = TRACETST(TRACE_OBJNAME) ? (current_object && current_object->name ? current_object->name : "??") : ""; sprintf(buf, "*** %d %*s %s %s %s%s", tracedepth, tracedepth, "", msg, objname, fname, post); write_socket(buf, command_giver); } static void resolve_table(struct program *prog, struct fkntab *tab) { int i; for (i = 0; tab[i].name; i++) { if (search_for_function(tab[i].name, prog)) { tab[i].inherit_index = function_inherit_found; tab[i].function_index = function_index_found; } else { tab[i].inherit_index = (unsigned short)-1; tab[i].function_index = (unsigned short)-1; } } } #include "master.t" struct svalue * apply_master_ob(int fun, int num_arg) { extern struct object *master_ob; FILE *cf; static struct svalue retval = { T_NUMBER }; if (s_flag) num_mcall++; if (!master_ob || master_fkntab[fun].inherit_index == (unsigned short)-1 && master_fkntab[fun].function_index == (unsigned short)-1) { pop_n_elems(num_arg); return 0; /* No such function */ } call_function(master_ob, master_fkntab[fun].inherit_index, master_fkntab[fun].function_index, num_arg); free_svalue(&retval); retval = *sp--; return &retval; } void master_ob_loaded() { extern struct object *master_ob; resolve_table(master_ob->prog, master_fkntab); } /*EOT*/ /* * When an object is destructed, all references to it must be removed * from the stack. */ void remove_object_from_stack(struct object *ob) { struct svalue *svp; for (svp = start_of_stack; svp <= sp; svp++) { if (svp->type != T_OBJECT) continue; if (svp->u.ob != ob) continue; free_object(svp->u.ob, "remove_object_from_stack"); svp->type = T_NUMBER; svp->u.number = 0; } } void stack_swap_objects(struct object *ob1, struct object *ob2) { struct control_stack *cspi; if (current_object == ob1) current_object = ob2; else if (current_object == ob2) current_object = ob1; if (previous_ob == ob1) previous_ob = ob2; else if (previous_ob == ob2) previous_ob = ob1; for (cspi = csp; cspi >= control_stack; cspi--) { if (cspi->ob == ob1) cspi->ob = ob2; else if (cspi->ob == ob2) cspi->ob = ob1; if (cspi->prev_ob == ob1) cspi->prev_ob = ob2; else if (cspi->prev_ob == ob2) cspi->prev_ob = ob1; } } static int strpref(char *p, char *s) { while (*p) if (*p++ != *s++) return 0; return 1; }