#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;
}