home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The World of Computer Software
/
World_Of_Computer_Software-02-387-Vol-3of3.iso
/
t
/
toaster.zip
/
Compat
/
interpret.c
< prev
next >
Wrap
C/C++ Source or Header
|
1992-01-06
|
144KB
|
5,707 lines
#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>
#ifdef MARK
#include <prof.h>
#endif
#include <memory.h>
#ifdef MARK
#define CASE(x) case x: MARK(x);
#else
#define CASE(x) case x:
#endif
#include "lint.h"
#include "lang.h"
#include "exec.h"
#include "interpret.h"
#include "config.h"
#include "object.h"
#include "wiz_list.h"
#include "instrs.h"
#include "patchlevel.h"
#include "comm.h"
#include "switch.h"
#ifdef RUSAGE /* Defined in config.h */
#include <sys/resource.h>
extern int getrusage PROT((int, struct rusage *));
#ifdef sun
extern int getpagesize();
#endif
#ifndef RUSAGE_SELF
#define RUSAGE_SELF 0
#endif
#endif
#if defined(__GNUC__) && !defined(lint)
#define INLINE /* inline */ /* Another time ! */
#else
#define INLINE
#endif
extern void print_svalue PROT((struct svalue *));
extern void say PROT((struct svalue *, struct vector *));
static struct svalue *sapply PROT((char *, struct object *, int));
static void do_trace PROT((char *, char *, char *));
static int apply_low PROT((char *, struct object *, int));
static int inter_sscanf PROT((int));
extern int remote_command;
static int strpref PROT((char *, char *));
extern struct object *obj_list_destruct;
extern struct object *previous_ob;
extern char *last_verb, *last_arg;
extern struct svalue const0, const1;
struct program *current_prog;
struct object *frame_ob;
extern int current_time;
extern struct object *current_heart_beat, *current_interactive;
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 isn't 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. */
static struct svalue *sp; /* Points to value of last push. */
static short *break_sp; /* Points to address to branch to
* at next F_BREAK */
static int function_index_offset; /* Needed for inheritance */
static int variable_index_offset; /* Needed for inheritance */
static 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];
static struct control_stack *csp; /* Points to last element pushed */
/*
* 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(ob)
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");
for (i=0; i < shadow->num_functions; i++) {
for (j=0; j < victim->num_functions; j++) {
if (shadow->functions[i].name != victim->functions[j].name)
continue;
if (victim->functions[j].type & TYPE_MOD_NO_MASK)
error("Illegal to shadow 'nomask' function \"%s\".\n",
victim->functions[j].name);
}
}
push_object(ob, "validate_shadowing");
ret = apply_master_ob("query_allow_shadow", 1);
if (!(ob->flags & O_DESTRUCTED) &&
ret && !(ret->type == T_NUMBER && ret->u.number == 0))
{
return 1;
}
return 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 on the stack.
*/
INLINE
void push_object(ob, from)
struct object *ob;
char *from;
{
char buf[50];
sp++;
if (sp == &start_of_stack[EVALUATOR_STACK_SIZE])
fatal("stack overflow\n");
sp->type = T_OBJECT;
sp->u.ob = ob;
if (from)
sprintf(buf, "push_object (%s)", from);
else strcpy(buf, "push_object");
add_ref(ob, buf);
}
/*
* Push a number on the value stack.
*/
INLINE
void push_number(n)
int n;
{
sp++;
if (sp == &start_of_stack[EVALUATOR_STACK_SIZE])
fatal("stack overflow\n");
sp->type = T_NUMBER;
sp->u.number = n;
}
/*
* Push a string on the value stack.
*/
INLINE
void push_string(p, type)
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(num)
int num;
{
#ifdef DEBUG
if (num >= current_object->prog->num_variables) {
fatal("Illegal variable access %d(%d). See trace above.\n",
num, current_object->prog->num_variables);
}
#endif
return ¤t_object->variables[num];
}
/*
* Free the data that an svalue is pointing to. Not the svalue
* itself.
*/
void free_svalue(v, from)
struct svalue *v;
char *from;
{
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:
{
char buf[80]; strcpy(buf, "free_svalue ");
if (from) { strcat(buf, "("); strcat(buf, from); strcat(buf, ")"); }
free_object(v->u.ob, buf);
break;
}
case T_POINTER:
free_vector(v->u.vec);
break;
}
*v = const0;
}
#ifndef COMPAT_MODE
/*
* Prepend a slash in front of a string.
*/
static char *add_slash(str)
char *str;
{
char *tmp;
tmp = xalloc(strlen(str)+2);
strcpy(tmp,"/"); strcat(tmp,str);
return tmp;
}
#endif
/*
* 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(to, from)
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 */
to->u.string = string_copy(from->u.string);
break;
case STRING_CONSTANT: /* Good idea to make it shared */
to->string_type = STRING_SHARED;
/* FALL THROUGH ! */
case STRING_SHARED: /* It already is shared */
to->u.string = make_shared_string(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;
}
}
INLINE void assign_svalue(dest, v)
struct svalue *dest;
struct svalue *v;
{
/* First deallocate the previous value. */
free_svalue(dest, "assign_svalue");
assign_svalue_no_free(dest, v);
}
void push_svalue(v)
struct svalue *v;
{
sp++;
assign_svalue_no_free(sp, v);
}
static INLINE void pop_stack() {
#ifdef DEBUG
if (sp < start_of_stack)
fatal("Stack underflow.\n");
#endif
free_svalue(sp, "pop_stack");
sp--;
}
/*
* Compute the address of an array element.
*/
static INLINE void push_indexed_lvalue()
{
struct svalue *vec, *item;
int ind;
ind = sp->u.number;
vec = sp - 1;
if (sp->type != T_NUMBER || ind < 0)
error("Illegal index\n");
pop_stack();
if (vec->type == T_STRING) {
static struct svalue one_character;
/* marion says: this is a crude part of code */
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, "push_indexed_lvalue");
sp->type = T_LVALUE;
sp->u.lvalue = &one_character;
return;
}
if (vec->type != T_POINTER) error("Indexing on illegal type.\n");
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, "push_indexed_lvalue");
/* This will make 'vec' invalid to use */
sp->type = T_LVALUE;
sp->u.lvalue = item;
}
#ifdef OPCPROF
#define MAXOPC 512
static int opcount[MAXOPC];
#endif
/*
* Deallocate 'n' values from the stack.
*/
INLINE
void pop_n_elems(n)
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(arg, instr)
int arg, instr;
{
error("Bad argument %d to %s()\n", arg, get_f_name(instr));
}
INLINE
static void push_control_stack(funp)
struct function *funp;
{
if (csp == &control_stack[MAX_TRACE-1])
error("Too deep recursion.\n");
csp++;
csp->funp = funp; /* Only used for tracebacks */
csp->ob = current_object;
csp->prev_ob = previous_ob;
csp->fp = fp;
csp->prog = current_prog;
/* csp->extern_call = 0; It is set by eval_instruction() */
csp->pc = pc;
csp->function_index_offset = function_index_offset;
csp->variable_index_offset = variable_index_offset;
csp->break_sp = break_sp;
}
/*
* Pop the control stack one element, and restore registers.
* extern_call must not be modified here, as it is used imediately after pop.
*/
static void pop_control_stack() {
#ifdef DEBUG
if (csp == control_stack - 1)
fatal("Popped out of the control stack");
#endif
current_object = csp->ob;
current_prog = csp->prog;
previous_ob = csp->prev_ob;
pc = csp->pc;
fp = csp->fp;
function_index_offset = csp->function_index_offset;
variable_index_offset = csp->variable_index_offset;
break_sp = csp->break_sp;
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(v)
struct vector *v;
{
v->ref++;
sp++;
sp->type = T_POINTER;
sp->u.vec = v;
}
/*
* Push a string on the stack that is already shared.
*/
static void INLINE push_shared_string(p)
char *p;
{
sp++;
sp->type = T_STRING;
sp->u.string = p;
sp->string_type = STRING_SHARED;
}
/*
* Push a string on the stack that is already malloced.
*/
static void INLINE push_malloced_string(p)
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(p)
char *p;
{
sp++;
sp->type = T_STRING;
sp->u.string = p;
sp->string_type = STRING_CONSTANT;
}
static void do_trace_call(funp)
struct function *funp;
{
do_trace("Call direct ", funp->name, " ");
if (TRACEHB) {
if (TRACETST(TRACE_ARGS)) {
int i;
add_message(" with %d arguments: ", funp->num_arg);
for(i = funp->num_arg-1; i >= 0; i--) {
print_svalue(&sp[-i]);
add_message(" ");
}
}
add_message("\n");
}
}
/*
* Argument is the function to execute. If it is defined by inheritance,
* then search for the reall definition, and return it.
* There is a number of arguments on the stack. Normalize them and initialize
* local variables, so that the called function is pleased.
*/
static struct function *setup_new_frame(funp)
struct function *funp;
{
function_index_offset = 0;
variable_index_offset = 0;
while(funp->flags & NAME_INHERITED) {
function_index_offset +=
current_prog->inherit[funp->offset].function_index_offset;
variable_index_offset +=
current_prog->inherit[funp->offset].variable_index_offset;
current_prog =
current_prog->inherit[funp->offset].prog;
funp = ¤t_prog->functions[funp->function_index_offset];
}
/* Remove excessive arguments */
while(csp->num_local_variables > funp->num_arg) {
pop_stack();
csp->num_local_variables--;
}
/* Correct number of arguments and local variables */
while(csp->num_local_variables < funp->num_arg + funp->num_local) {
push_number(0);
csp->num_local_variables++;
}
tracedepth++;
if (TRACEP(TRACE_CALL)) {
do_trace_call(funp);
}
fp = sp - csp->num_local_variables + 1;
break_sp = (short*)(sp+1);
return funp;
}
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.
*/
void push_pop_error_context (push)
int push;
{
extern jmp_buf error_recovery_context;
extern int error_recovery_context_exists;
extern struct svalue catch_value;
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;
} *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);
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 {
if (p = ecsp) {
if (!push) {
#ifdef DEBUG
if (csp != p->save_csp-1)
fatal("Catch: Lost track of csp");
if (command_giver != p->save_command_giver)
fatal("Catch: Lost track of command_giver");
#endif
} else {
/*
* 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;
free ((char *)p);
} else fatal("Catch: error context stack underflow");
}
}
static struct vector *append_vector(v, a)
struct vector *v;
struct svalue *a;
{
int i;
struct vector *new;
new = allocate_array(v->size+1);
for (i=0; i<v->size; ++i)
assign_svalue(&new->item[i], &v->item[i]);
assign_svalue(&new->item[i], a);
return new;
}
static struct vector *prepend_vector(v, a)
struct vector *v;
struct svalue *a;
{
int i;
struct vector *new;
new = allocate_array(v->size+1);
assign_svalue(&new->item[0], a);
for (i=0; i<v->size; ++i)
assign_svalue(&new->item[i+1], &v->item[i]);
return new;
}
/*
* When a vector is given as argument to an efun, all items has to be
* checked if there would be an destructed object.
* A bad problem currently is that a vector can contain another vector, so this
* should be tested too. But, there is currently no prevention against
* recursive vectors, which means that this can not be tested. Thus, the game
* may crash if a vector contains a vector that contains a destructed object
* and this top-most vector is used as an argument to an efun.
*/
/* The game won't crash when doing simple operations like assign_svalue
* on a destructed object. You have to watch out, of course, that you don't
* apply a function to it.
* to save space it is preferable that destructed objects are freed soon.
* amylaar
*/
void check_for_destr(v)
struct vector *v;
{
int i;
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);
}
}
/*
* 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 won't 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 void eval_instruction(p)
char *p;
{
struct object *ob;
int i, num_arg;
int instruction;
#ifdef DEBUG
struct svalue *expected_stack;
#endif
struct svalue *argp;
/* Next F_RETURN at this level will return out of eval_instruction() */
csp->extern_call = 1;
pc = p;
again:
instruction = EXTRACT_UCHAR(pc);
#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
pc++;
if (current_object && current_object->user)
current_object->user->cost++;
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.
*/
if (instrs[instruction].min_arg != instrs[instruction].max_arg) {
num_arg = EXTRACT_UCHAR(pc);
pc++;
if (num_arg > 0) {
if (instrs[instruction].type[0] != 0 &&
(instrs[instruction].type[0] & (sp-num_arg+1)->type) == 0) {
bad_arg(1, instruction + F_OFFSET);
}
}
if (num_arg > 1) {
if (instrs[instruction].type[1] != 0 &&
(instrs[instruction].type[1] & (sp-num_arg+2)->type) == 0) {
bad_arg(2, instruction + F_OFFSET);
}
}
} else {
num_arg = instrs[instruction].min_arg;
if (instrs[instruction].min_arg > 0) {
if (instrs[instruction].type[0] != 0 &&
(instrs[instruction].type[0] & (sp-num_arg+1)->type) == 0) {
bad_arg(1, instruction + F_OFFSET);
}
}
if (instrs[instruction].min_arg > 1) {
if (instrs[instruction].type[1] != 0 &&
(instrs[instruction].type[1] & (sp-num_arg+2)->type) == 0) {
bad_arg(2, instruction + F_OFFSET);
}
}
/*
* Safety measure. It is supposed that the evaluator knows
* the number of arguments.
*/
num_arg = -1;
}
#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.
*/
if (TRACEP(TRACE_EXEC)) {
do_trace("Exec ", get_f_name(instruction), "\n");
}
switch(instruction) {
default:
fatal("Undefined instruction %s (%d)\n", get_f_name(instruction),
instruction);
/*NOTREACHED*/
CASE(F_REMOVE_SHADOW);
{
ob = current_object;
if (num_arg) { ob = sp->u.ob; pop_stack(); }
if (!ob || !ob->shadowing)
push_number(0);
else
{
if (ob->shadowed)
ob->shadowed->shadowing = ob->shadowing;
if (ob->shadowing)
ob->shadowing->shadowed = ob->shadowed;
ob->shadowing = ob->shadowed = 0;
push_object(ob, "F_REMOVE_SHADOW");
}
break;
}
CASE(F_REGEXP);
{
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 */
}
break;
}
CASE(F_SHADOW);
{
struct svalue *ret;
struct object *top;
top = ob = (sp-1)->u.ob;
if (sp->u.number == 0) {
ob = ob->shadowed;
pop_n_elems(2);
if (ob)
push_object(ob, "F_SHADOW");
else push_number(0);
break;
}
if (validate_shadowing(ob)) {
/*
* The shadow is entered first in the chain.
*/
while (ob->shadowed)
ob = ob->shadowed;
current_object->shadowing = ob;
ob->shadowed = current_object;
current_object->shadowed = 0;
pop_n_elems(2);
push_object(top, "F_SHADOW");
}
else
{
pop_n_elems(2);
push_number(0);
}
break;
}
CASE(F_POP_VALUE);
pop_stack();
break;
CASE(F_DUP);
sp++;
assign_svalue_no_free(sp, sp-1);
break;
CASE(F_JUMP_WHEN_ZERO);
{
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();
break;
}
CASE(F_JUMP);
{
unsigned short offset;
((char *)&offset)[0] = pc[0];
((char *)&offset)[1] = pc[1];
pc = current_prog->program + offset;
break;
}
CASE(F_JUMP_WHEN_NON_ZERO);
{
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();
break;
}
CASE(F_INDIRECT);
#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, "F_INDIRECT");
*sp = const0;
}
break;
CASE(F_IDENTIFIER);
sp++;
assign_svalue_no_free(sp, find_value((int)(EXTRACT_UCHAR(pc) +
variable_index_offset)));
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, "F_ID");
*sp = const0;
}
break;
CASE(F_PUSH_IDENTIFIER_LVALUE);
sp++;
sp->type = T_LVALUE;
sp->u.lvalue = find_value((int)(EXTRACT_UCHAR(pc) +
variable_index_offset));
pc++;
break;
CASE(F_PUSH_INDEXED_LVALUE);
push_indexed_lvalue();
break;
CASE(F_INDEX_INSTR);
push_indexed_lvalue();
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, "F_INDEX_INSTR");
sp->type = T_NUMBER;
sp->u.number = 0;
}
break;
CASE(F_LOCAL_NAME);
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, "F_LOCAL_NAME");
*sp = const0;
}
break;
CASE(F_PUSH_LOCAL_VARIABLE_LVALUE);
sp++;
sp->type = T_LVALUE;
sp->u.lvalue = fp + EXTRACT_UCHAR(pc);
pc++;
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--;
if (TRACEP(TRACE_RETURN)) {
do_trace("Return", "", "");
if (TRACEHB) {
if (TRACETST(TRACE_ARGS)) {
add_message(" with value: ");
print_svalue(sp);
}
add_message("\n");
}
}
if (csp[1].extern_call) /* The control stack was popped just before */
return;
break;
}
CASE(F_BREAK_POINT);
break_point(); /* generated by lang.y when -d. Will check stack. */
break;
CASE(F_CHECK_REF);
{
int a = -1;
if (num_arg == 2)
{
if ((sp-1)->type == T_OBJECT)
a = (sp-1)->u.ob->ref;
}
else if (sp->type == T_OBJECT)
a = sp->u.ob->prog->ref;
else if (sp->type == T_POINTER)
a = sp->u.vec->ref;
pop_n_elems(num_arg);
push_number(a);
break;
}
CASE(F_CLONE_OBJECT);
ob = clone_object(sp->u.string);
pop_stack();
if (ob) {
push_object(ob, "F_CLONE_OBJECT");
} else {
push_number(0);
}
break;
CASE(F_AGGREGATE);
{
struct vector *v;
unsigned short num;
((char *)&num)[0] = pc[0];
((char *)&num)[1] = pc[1];
pc += 2;
v = allocate_array((int)num);
for (i=0; i < 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 */
break;
}
CASE(F_TAIL);
if (tail(sp->u.string))
assign_svalue(sp, &const1);
else
assign_svalue(sp, &const0);
break;
CASE(F_CALL_FUNCTION_BY_ADDRESS);
{
unsigned short func_index;
struct function *funp;
((char *)&func_index)[0] = pc[0];
((char *)&func_index)[1] = pc[1];
pc += 2;
func_index += function_index_offset;
/*
* Find the function in the function table. As the function may have
* been redefined by inheritance, we must look in the last table,
* which is pointed to by current_object.
*/
#ifdef DEBUG
if (func_index >= current_object->prog->num_functions)
fatal("Illegal function index\n");
#endif
/* NOT current_prog, which can be an inherited object. */
funp = ¤t_object->prog->functions[func_index];
if (funp->flags & NAME_UNDEFINED)
error("Undefined function: %s\n", funp->name);
/* Save all important global stack machine registers */
push_control_stack(funp); /* return pc is adjusted later */
/* This assigment must be done after push_control_stack() */
current_prog = current_object->prog;
/*
* If it is an inherited function, search for the real
* definition.
*/
csp->num_local_variables = EXTRACT_UCHAR(pc);
pc++;
funp = setup_new_frame(funp);
csp->pc = pc; /* The corrected return address */
pc = current_prog->program + funp->offset;
csp->extern_call = 0;
break;
}
CASE(F_SAVE_OBJECT);
save_object(current_object, sp->u.string);
/* The argument is returned */
break;
CASE(F_FIND_OBJECT);
ob = find_object2(sp->u.string);
pop_stack();
if (ob)
push_object(ob, "F_FIND_OBJECT");
else
push_number(0);
break;
CASE(F_FIND_PLAYER);
ob = find_living_object(sp->u.string, 1);
pop_stack();
if (!ob)
push_number(0);
else
push_object(ob, "F_FIND_PLAYER");
break;
CASE(F_WRITE_FILE);
i = write_file((sp-1)->u.string, sp->u.string);
pop_n_elems(2);
push_number(i);
break; /* Return first argument */
CASE(F_READ_BYTES);
{
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, instruction);
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_string(str, STRING_MALLOC);
free(str);
}
break;
}
CASE(F_WRITE_BYTES);
i = write_bytes((sp-2)->u.string, (sp-1)->u.number, sp->u.string);
pop_n_elems(3);
push_number(i);
break;
CASE(F_FILE_SIZE);
i = file_size(sp->u.string);
pop_stack();
push_number(i);
break;
CASE(F_FIND_LIVING);
ob = find_living_object(sp->u.string, 0);
pop_stack();
if (!ob)
push_number(0);
else
push_object(ob, "F_FIND_LIVING");
break;
CASE(F_TELL_OBJECT);
tell_object((sp-1)->u.ob, sp->u.string);
pop_stack(); /* Return first argument */
break;
CASE(F_RESTORE_OBJECT);
i = restore_object(current_object, sp->u.string);
pop_stack();
push_number(i);
break;
CASE(F_THIS_PLAYER);
if (num_arg && current_interactive &&
!(current_interactive->flags & O_DESTRUCTED) )
push_object(current_interactive, "F_THIS_PLAYER");
else if (command_giver && !(command_giver->flags & O_DESTRUCTED))
push_object(command_giver, "F_THIS_PLAYER");
else
push_number(0);
break;
CASE(F_FIRST_INVENTORY);
ob = first_inventory(sp);
pop_stack();
if (ob)
push_object(ob, "F_FIRST_INVENTORY");
else
push_number(0);
break;
CASE(F_LIVING);
if (sp->u.ob->flags & O_ENABLE_COMMANDS)
assign_svalue(sp, &const1);
else
assign_svalue(sp, &const0);
break;
#ifdef FGETUID
CASE(F_GETUID);
ob = sp->u.ob;
#ifdef DEBUG
if (ob->user == 0)
fatal("User is null pointer\n");
#endif
{ char *tmp;
tmp = ob->user->name;
pop_stack();
push_string(tmp, STRING_CONSTANT);
}
break;
#endif F_GETUID
#ifdef F_GETEUID
CASE(F_GETEUID);
/*
* Are there any reasons to support this one in -o mode ?
*/
ob = sp->u.ob;
if (ob->eff_user) {
char *tmp;
tmp = ob->eff_user->name;
pop_stack();
push_string(tmp, STRING_CONSTANT);
}
else {
pop_stack();
push_number(0);
}
break;
#endif /* F_GETEUID */
#ifdef F_EXPORT_UID
CASE(F_EXPORT_UID);
if (current_object->eff_user==0)
error("Illegal to export uid.\n");
ob = sp->u.ob;
if (ob->eff_user) /* Only allowed to export when null */
break;
ob->user = current_object->eff_user;
break;
#endif /* F_EXPORT_UID */
#ifdef F_SETEUID
CASE(F_SETEUID);
{
struct svalue *ret;
if (sp->type == T_NUMBER) {
if (sp->u.number != 0)
bad_arg(1, F_SETEUID);
current_object->eff_user = 0;
pop_stack();
push_number(1);
break;
}
argp = sp;
if (argp->type != T_STRING)
bad_arg(1, F_SETEUID);
push_object(current_object, "F_SETEUID");
push_string(argp->u.string, STRING_CONSTANT);
ret = apply_master_ob("valid_seteuid", 2);
if (ret == 0 || ret->type != T_NUMBER || ret->u.number != 1) {
pop_stack();
push_number(0);
break;
}
current_object->eff_user = add_name(argp->u.string);
pop_stack();
push_number(1);
break;
}
#endif
#ifdef F_CREATOR
CASE(F_CREATOR);
ob = sp->u.ob;
if (ob->user == 0) {
assign_svalue(sp, &const0);
} else {
pop_stack();
push_string(ob->user->name, STRING_CONSTANT);
}
break;
#endif
CASE(F_SHUTDOWN);
{
extern struct object *master_ob;
assert_master_ob_loaded();
if (current_object != master_ob)
error("shutdown() can only be called from master.c\n");
startshutdowngame();
push_number(0);
break;
}
CASE(F_EXPLODE);
{
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);
}
break;
}
CASE(F_FILTER_ARRAY);
{
struct vector *v;
struct svalue *arg;
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 type argument 3 to filter_array()\n");
if (arg[0].type == T_POINTER) {
check_for_destr(arg[0].u.vec);
v = filter(arg[0].u.vec, arg[1].u.string, ob,
num_arg > 3 ? sp : (struct svalue *)0);
} else {
v = 0;
}
pop_n_elems(num_arg);
if (v) {
push_vector(v); /* This will make ref count == 2 */
v->ref--;
} else {
push_number(0);
}
break;
}
CASE(F_SET_BIT);
{
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;
break;
}
CASE(F_CLEAR_BIT);
{
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();
break;
}
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;
break;
}
CASE(F_TEST_BIT);
{
int len;
len = strlen((sp-1)->u.string);
if (sp->u.number/6 >= len) {
pop_n_elems(2);
push_number(0);
break;
}
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);
}
break;
}
CASE(F_QUERY_LOAD_AVERAGE);
push_string(query_load_av(), STRING_SHARED); /**MALLOC**/
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;
/*
* 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;
push_control_stack((struct function *)0);
csp->num_local_variables = 0; /* No extra variables */
csp->pc = current_prog->program + new_pc_offset;
csp->num_local_variables = (csp-1)->num_local_variables; /* marion */
/*
* Save some global variables that must be restored separately
* after a longjmp. The stack will have to be manually popped all
* the way.
*/
push_pop_error_context (1);
/* 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);
pop_control_stack();
assign_svalue_no_free(++sp, &catch_value);
}
/* next error will return 1 by default */
assign_svalue(&catch_value, &const1);
break;
}
CASE(F_THROW);
/* marion
* the return from catch is now done by a 0 throw
*/
assign_svalue(&catch_value, sp--);
if (catch_value.type == T_NUMBER && catch_value.u.number == 0) {
/* We come here when no longjmp() was executed. */
pop_control_stack();
push_pop_error_context (0);
push_number(0);
} else throw_error(); /* do the longjump, with extra checks... */
break;
CASE(F_NOTIFY_FAIL);
set_notify_fail_message(sp->u.string);
/* Return the argument */
break;
CASE(F_QUERY_IDLE);
i = query_idle(sp->u.ob);
pop_stack();
push_number(i);
break;
CASE(F_IMPLODE);
{
char *str;
check_for_destr((sp-1)->u.vec);
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_SHARED;
sp->u.string = make_shared_string(str); /**MALLOC**/
free(str);
} else {
push_number(0);
}
break;
}
CASE(F_QUERY_SNOOP);
{
struct svalue *arg1;
extern struct object *master_ob;
if (command_giver == 0 || sp->u.ob->interactive == 0) {
assign_svalue(sp, &const0);
break;
}
#ifdef COMPAT_MODE
arg1 = sapply("query_level", command_giver, 0);
if (arg1 == 0 || arg1->type != T_NUMBER ||
arg1->u.number < QUERY_SNOOP_LEVEL)
{
assign_svalue(sp, &const0);
break;
}
ob = query_snoop(sp->u.ob);
#else
assert_master_ob_loaded();
if (current_object == master_ob)
ob = query_snoop(sp->u.ob);
else
ob = 0;
#endif
pop_stack();
if (ob)
push_object(ob, "F_QUERY_SNOOP");
else
push_number(0);
break;
}
CASE(F_QUERY_IP_NUMBER);
CASE(F_QUERY_IP_NAME);
{
extern char *query_ip_number PROT((struct object *));
extern char *query_ip_name PROT((struct object *));
char *tmp;
if (num_arg == 1 && sp->type != T_OBJECT)
error("Bad optional argument to query_ip_number()\n");
if (instruction == F_QUERY_IP_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_SHARED); /**MALLOC**/
break;
}
CASE(F_QUERY_HOST_NAME);
{
extern char *query_host_name();
char *tmp;
tmp = query_host_name();
if (tmp)
push_string(tmp, STRING_CONSTANT);
else
push_number(0);
break;
}
CASE(F_NEXT_INVENTORY);
ob = sp->u.ob;
pop_stack();
if (ob->next_inv)
push_object(ob->next_inv, "F_NEXT_INVENTORY");
else
push_number(0);
break;
CASE(F_ALL_INVENTORY);
case F_CONTENTS:
{
struct vector *vec;
struct object *ob = current_object;
if (num_arg)
{
if (sp->type == T_OBJECT)
ob = sp->u.ob;
else
ob = find_object(sp->u.string);
pop_stack();
if (!ob)
{
push_number(0);
break;
}
}
vec = all_inventory(ob);
if (vec == 0) {
push_number(0);
} else {
push_vector(vec); /* This will make ref count == 2 */
vec->ref--;
}
break;
}
CASE(F_DEEP_INVENTORY);
{
struct vector *vec;
vec = deep_inventory(sp->u.ob, 0);
free_svalue(sp, "F_DEEP_INVENTORY");
sp->type = T_POINTER;
sp->u.vec = vec;
break;
}
CASE(F_ENVIRONMENT);
if (num_arg) {
ob = environment(sp);
pop_stack();
} else if (!(current_object->flags & O_DESTRUCTED)) {
ob = current_object->super;
} else
ob = 0;
if (ob)
{
push_object(ob, "F_ENVIRONMENT");
}
else
push_number(0);
break;
CASE(F_THIS_OBJECT);
push_object(current_object, "F_THIS_OBJECT");
break;
CASE(F_PREVIOUS_OBJECT);
if (previous_ob == 0 || (previous_ob->flags & O_DESTRUCTED))
push_number(0);
else
push_object(previous_ob, "F_PREVIOUS_OBJECT");
break;
CASE(F_LOCALCMD);
print_local_commands();
push_number(0);
break;
CASE(F_SWAP);
(void)swap(sp->u.ob);
break;
CASE(F_TRACE);
{
int ot = -1;
if (command_giver && command_giver->interactive) {
struct svalue *arg;
push_constant_string("trace");
arg = apply_master_ob("query_player_level", 1);
if (arg && (arg->type != T_NUMBER || arg->u.number != 0)) {
ot = command_giver->interactive->trace_level;
command_giver->interactive->trace_level = sp->u.number;
}
}
pop_stack();
push_number(ot);
}
break;
CASE(F_TRACEPREFIX);
{
char *old = 0;
if (command_giver && command_giver->interactive) {
struct svalue *arg;
push_constant_string("trace");
arg = apply_master_ob("query_player_level",1);
if (arg && (arg->type != T_NUMBER || arg->u.number)) {
old = command_giver->interactive->trace_prefix;
if (sp->type == T_STRING) {
command_giver->interactive->trace_prefix =
make_shared_string(sp->u.string);
} else
command_giver->interactive->trace_prefix = 0;
}
}
pop_stack();
if (old) {
push_string(old, STRING_SHARED); /* Will incr ref count */
free_string(old);
} else {
push_number(0);
}
}
break;
CASE(F_TIME);
push_number(current_time);
break;
CASE(F_WIZLIST);
if (num_arg) {
wizlist(sp->u.string);
} else {
wizlist((char *)0);
push_number(0);
}
break;
#ifdef F_TRANSFER
CASE(F_TRANSFER);
{
struct object *dest;
if (sp->type == T_STRING) {
dest = find_object(sp->u.string);
if (dest == 0)
error("Object not found.\n");
} else {
dest = sp->u.ob;
}
i = transfer_object((sp-1)->u.ob, dest);
pop_n_elems(2);
push_number(i);
break;
}
#endif
CASE(F_ADD_WORTH);
if (strncmp(current_object->name, "obj/", 4) != 0 &&
strncmp(current_object->name, "std/", 4) != 0 &&
strncmp(current_object->name, "room/", 5) != 0)
error("Illegal call of add_worth.\n");
if (num_arg == 2) {
if (sp->u.ob->user)
sp->u.ob->user->total_worth += (sp-1)->u.number;
pop_stack();
} else {
if (previous_ob == 0)
break;
if (previous_ob->user)
previous_ob->user->total_worth += sp->u.number;
}
break;
CASE(F_ADD);
{
struct svalue ret;
switch((sp-1)->type)
{
case T_STRING:
{
switch(sp->type)
{
case T_STRING:
{
char *res;
res=xalloc(strlen((sp-1)->u.string)+strlen(sp->u.string)+1);
(void)strcpy(res, (sp-1)->u.string);
(void)strcat(res, sp->u.string);
ret.type = T_STRING;
ret.string_type = STRING_SHARED;
ret.u.string = make_shared_string(res);
free(res);
break;
}
case T_NUMBER:
{
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);
ret.type = T_STRING;
ret.string_type = STRING_SHARED;
ret.u.string = make_shared_string(res);
free(res);
break;
}
case T_POINTER:
ret.type = T_POINTER;
ret.u.vec = prepend_vector(sp->u.vec, sp-1);
break;
default: error("Bad type argument to +. %d %d\n",
(sp-1)->type, sp->type);
}
break;
}
case T_NUMBER:
{
ret.type = sp->type;
switch(sp->type)
{
case T_NUMBER:
ret.u.number = sp->u.number + (sp-1)->u.number;
break;
case 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);
ret.string_type = STRING_SHARED;
ret.u.string = make_shared_string(res);
free(res);
break;
}
case T_POINTER:
ret.u.vec = prepend_vector(sp->u.vec, sp-1);
break;
default: error("Bad type argument to +. %d %d\n",
(sp-1)->type, sp->type);
}
break;
}
case T_POINTER:
{
ret.type = T_POINTER;
switch(sp->type)
{
case T_POINTER:
ret.u.vec = add_array((sp-1)->u.vec, sp->u.vec);
break;
case T_NUMBER:
case T_STRING:
ret.u.vec = append_vector((sp-1)->u.vec, sp);
break;
default: error("Bad type argument to +. %d %d\n",
(sp-1)->type, sp->type);
}
break;
}
default: error("Bad type argument to +. %d %d\n",
(sp-1)->type, sp->type);
}
pop_n_elems(2);
push_svalue(&ret);
free_svalue(&ret, "F_ADD");
break;
}
CASE(F_SUBTRACT);
{
struct svalue ret;
ret.type = (sp-1)->type;
switch((sp-1)->type)
{
case T_NUMBER:
{
switch(sp->type)
{
case T_NUMBER:
ret.u.number = (sp-1)->u.number - sp->u.number;
break;
default:
bad_arg(2, F_SUBTRACT);
break;
}
break;
}
case T_POINTER:
#ifdef COMPAT_MODE
ret.u.vec = subtract_array((sp-1)->u.vec, sp);
#else
{
if (sp->type == T_POINTER) {
extern struct vector *subtract_array
PROT((struct vector *,struct vector*));
struct vector *v;
v = sp->u.vec;
if (v->ref > 1) {
v = slice_array(v, 0, v->size-1 );
v->ref--;
}
sp--;
v = subtract_array(sp->u.vec, v);
free_vector(sp->u.vec);
sp->u.vec = v;
}
else bad_arg(2, F_SUBTRACT);
}
#endif
break;
default:
bad_arg(1, F_SUBTRACT);
}
pop_n_elems(2);
push_svalue(&ret);
free_svalue(&ret, "F_SUBTRACT");
break;
}
CASE(F_AND);
if (sp->type == T_POINTER && (sp-1)->type == T_POINTER) {
extern struct vector *intersect_array
PROT((struct vector *, struct vector *));
(sp-1)->u.vec = intersect_array(sp->u.vec, (sp-1)->u.vec);
sp--;
break;
}
if ((sp-1)->type != T_NUMBER)
bad_arg(1, F_AND);
if (sp->type != T_NUMBER)
bad_arg(2, F_AND);
i = (sp-1)->u.number & sp->u.number;
sp--;
sp->u.number = i;
break;
CASE(F_OR);
if ((sp-1)->type != T_NUMBER)
bad_arg(1, F_OR);
if (sp->type != T_NUMBER)
bad_arg(2, F_OR);
i = (sp-1)->u.number | sp->u.number;
sp--;
sp->u.number = i;
break;
CASE(F_XOR);
if ((sp-1)->type != T_NUMBER)
bad_arg(1, instruction);
if (sp->type != T_NUMBER)
bad_arg(2, instruction);
i = (sp-1)->u.number ^ sp->u.number;
sp--;
sp->u.number = i;
break;
CASE(F_LSH);
if ((sp-1)->type != T_NUMBER)
bad_arg(1, instruction);
if (sp->type != T_NUMBER)
bad_arg(2, instruction);
i = (sp-1)->u.number << sp->u.number;
sp--;
sp->u.number = i;
break;
CASE(F_RSH);
if ((sp-1)->type != T_NUMBER)
bad_arg(1, instruction);
if (sp->type != T_NUMBER)
bad_arg(2, instruction);
i = (sp-1)->u.number >> sp->u.number;
sp--;
sp->u.number = i;
break;
CASE(F_MULTIPLY);
if ((sp-1)->type != T_NUMBER)
bad_arg(1, instruction);
if (sp->type != T_NUMBER)
bad_arg(2, instruction);
i = (sp-1)->u.number * sp->u.number;
sp--;
sp->u.number = i;
break;
CASE(F_DIVIDE);
if ((sp-1)->type != T_NUMBER)
bad_arg(1, instruction);
if (sp->type != T_NUMBER)
bad_arg(2, instruction);
if (sp->u.number == 0)
error("Division by zero\n");
i = (sp-1)->u.number / sp->u.number;
sp--;
sp->u.number = i;
break;
CASE(F_MOD);
if ((sp-1)->type != T_NUMBER)
bad_arg(1, instruction);
if (sp->type != T_NUMBER)
bad_arg(2, instruction);
if (sp->u.number == 0)
error("Modulus by zero.\n");
i = (sp-1)->u.number % sp->u.number;
sp--;
sp->u.number = i;
break;
CASE(F_GT);
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);
break;
}
if ((sp-1)->type != T_NUMBER)
bad_arg(1, instruction);
if (sp->type != T_NUMBER)
bad_arg(2, instruction);
i = (sp-1)->u.number > sp->u.number;
sp--;
sp->u.number = i;
break;
CASE(F_GE);
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);
break;
}
if ((sp-1)->type != T_NUMBER)
bad_arg(1, instruction);
if (sp->type != T_NUMBER)
bad_arg(2, instruction);
i = (sp-1)->u.number >= sp->u.number;
sp--;
sp->u.number = i;
break;
CASE(F_LT);
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);
break;
}
if ((sp-1)->type != T_NUMBER)
bad_arg(1, instruction);
if (sp->type != T_NUMBER)
bad_arg(2, instruction);
i = (sp-1)->u.number < sp->u.number;
sp--;
sp->u.number = i;
break;
CASE(F_LE);
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);
break;
}
if ((sp-1)->type != T_NUMBER)
bad_arg(1, instruction);
if (sp->type != T_NUMBER)
bad_arg(2, instruction);
i = (sp-1)->u.number <= sp->u.number;
sp--;
sp->u.number = i;
break;
CASE(F_EQ);
if ((sp-1)->type != sp->type) {
pop_stack();
assign_svalue(sp, &const0);
break;
}
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_STRING:
i = strcmp((sp-1)->u.string, sp->u.string) == 0;
break;
case T_OBJECT:
i = (sp-1)->u.ob == sp->u.ob;
break;
default:
i = 0;
break;
}
pop_n_elems(2);
push_number(i);
break;
CASE(F_NE);
if ((sp-1)->type != sp->type) {
pop_stack();
assign_svalue(sp, &const1);
break;
}
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_OBJECT:
i = (sp-1)->u.ob != sp->u.ob;
break;
default:
fatal("Illegal type to !=\n");
}
pop_n_elems(2);
push_number(i);
break;
CASE(F_LOG_FILE);
log_file((sp-1)->u.string, sp->u.string);
pop_stack();
break; /* Return first argument */
CASE(F_NOT);
if (sp->type == T_NUMBER && sp->u.number == 0)
sp->u.number = 1;
else
assign_svalue(sp, &const0);
break;
CASE(F_COMPL);
if (sp->type != T_NUMBER)
error("Bad argument to ~\n");
sp->u.number = ~ sp->u.number;
break;
CASE(F_NEGATE);
if (sp->type != T_NUMBER)
error("Bad argument to unary minus\n");
sp->u.number = - sp->u.number;
break;
CASE(F_INC);
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);
break;
CASE(F_DEC);
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);
break;
CASE(F_POST_INC);
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--;
break;
CASE(F_POST_DEC);
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++;
break;
CASE(F_CALL_OTHER);
{
struct svalue *arg, tmp;
arg = sp - num_arg + 1;
if (arg[0].type == T_OBJECT)
ob = arg[0].u.ob;
else {
ob = find_object(arg[0].u.string);
if (ob == 0)
error("Could not find object %s\n", arg[0].u.string);
/* Raistlin -- Was this
fatal("find_object() returned 0\n"); */
}
if ((current_object->flags & O_DESTRUCTED) ||
(ob->flags & O_DESTRUCTED)) {
/*
* No external calls may be done when this object is
* destructed.
*/
pop_n_elems(num_arg);
push_number(0);
break;
}
if (arg[1].u.string[0] == ':')
error("Illegal function name in call_other: %s\n",
arg[1].u.string);
/*
* Send the remaining arguments to the function.
*/
if (TRACEP(TRACE_CALL_OTHER)) {
do_trace("Call other ", arg[1].u.string, "\n");
}
if (apply_low(arg[1].u.string, ob, num_arg-2) == 0) {
/* Function not found */
pop_n_elems(2);
push_number(0);
break;
}
/*
* 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 */
break;
}
CASE(F_INTP);
if (sp->type == T_NUMBER)
assign_svalue(sp, &const1);
else
assign_svalue(sp, &const0);
break;
CASE(F_STRINGP);
if (sp->type == T_STRING)
assign_svalue(sp, &const1);
else
assign_svalue(sp, &const0);
break;
CASE(F_OBJECTP);
if (sp->type == T_OBJECT)
assign_svalue(sp, &const1);
else
assign_svalue(sp, &const0);
break;
CASE(F_POINTERP);
if (sp->type == T_POINTER)
assign_svalue(sp, &const1);
else
assign_svalue(sp, &const0);
break;
CASE(F_EXTRACT);
{
int len, from, to;
struct svalue *arg;
char *res;
arg = sp - num_arg + 1;
if (num_arg == 1)
break; /* Simply return argument */
if (arg[0].type == T_POINTER)
{
struct vector *r;
int from, to, i;
from = arg[1].u.number;
if (num_arg == 3) to = arg[2].u.number;
else to = arg[0].u.vec->size-1;
if (from > arg[0].u.vec->size-1 || to < from) {
pop_n_elems(num_arg);
push_number(0);
break;
}
if (to > arg[0].u.vec->size-1)
to = arg[0].u.vec->size-1;
r = allocate_array(to - from + 1);
for (i = from; i <= to; ++i)
assign_svalue(&r->item[i-from], &arg[0].u.vec->item[i]);
pop_n_elems(num_arg);
push_vector(r);
r->ref--;
break;
}
len = strlen(arg[0].u.string);
from = arg[1].u.number;
if (from < 0)
from = len + from;
if (from >= len) {
pop_n_elems(num_arg);
push_string("", STRING_CONSTANT);
break;
}
if (num_arg == 2) {
res = make_shared_string(arg->u.string + from);
pop_n_elems(2);
push_shared_string(res); /**MALLOC**/
break;
}
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);
break;
}
if (to >= len)
to = len-1;
if (to == len-1) {
res = make_shared_string(arg->u.string + from);
pop_n_elems(3);
push_shared_string(res); /**MALLOC**/
break;
}
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_shared_string(make_shared_string(res)); /*MALLOC*/
free(res);
break;
}
CASE(F_ATOI);
{
int i = atoi(sp->u.string);
pop_stack();
push_number(i);
break;
}
CASE(F_RANGE);
{
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 >= len) {
pop_n_elems(3);
push_string("", STRING_CONSTANT);
break;
}
to = sp[0].u.number;
if (to < 0)
to = len + to;
if (to < from) {
pop_n_elems(3);
push_string("", STRING_CONSTANT);
break;
}
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);
break;
}
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");
}
break;
}
CASE(F_QUERY_ARG);
case F_THIS_ARG:
if (last_arg == 0) {
push_number(0);
break;
}
push_string(last_arg, STRING_CONSTANT);
break;
CASE(F_QUERY_VERB);
case F_THIS_VERB:
if (last_verb == 0) {
push_number(0);
break;
}
push_string(last_verb, STRING_CONSTANT);
break;
CASE(F_EXEC);
i =replace_interactive((sp-1)->u.ob, sp->u.ob, current_prog->name);
pop_n_elems(2);
push_number(i);
break;
CASE(F_FILE_NAME);
{
char *name,*res;
name = sp->u.ob->name;
#ifdef COMPAT_MODE
res = string_copy(name);
#else
res = add_slash(name);
#endif
pop_stack();
push_shared_string(make_shared_string(res)); /*MALLOC*/
free(res);
break;
}
CASE(F_USERS);
push_vector(users()); /* users() has already set ref count to 1 */
sp->u.vec->ref--;
break;
CASE(F_CALL_OUT);
{
struct svalue *arg = sp - num_arg + 1;
if (!(current_object->flags & O_DESTRUCTED))
new_call_out(current_object, arg[0].u.string, arg[1].u.number,
num_arg == 3 ? sp : 0);
pop_n_elems(num_arg);
push_number(0);
}
break;
CASE(F_CALL_OUT_INFO);
push_vector(get_all_call_outs());
sp->u.vec->ref--; /* Was set to 1 at allocation */
break;
CASE(F_REMOVE_CALL_OUT);
i = remove_call_out(current_object, sp->u.string);
pop_stack();
push_number(i);
break;
CASE(F_WRITE);
do_write(sp);
break;
CASE(F_MOVE_OBJECT);
{
struct object *o1, *o2;
if ((sp-1)->type == T_OBJECT)
o1 = (sp-1)->u.ob;
else {
o1 = find_object((sp-1)->u.string);
if (o1 == 0)
error("move_object failed\n");
}
if (sp->type == T_OBJECT)
o2 = sp->u.ob;
else {
o2 = find_object(sp->u.string);
if (o2 == 0)
error("move_object failed\n");
}
move_object(o1, o2);
pop_stack();
break;
}
CASE(F_FUNCTION_EXISTS);
{
char *str, *res;
str = function_exists((sp-1)->u.string, sp->u.ob);
pop_n_elems(2);
if (str) {
extern char *strrchr();
/* Raistlin -- made it use shared strings */
res = str;
/* marion - strip trailing .c */
if (str = strrchr (res, (int)'.')) *str = 0;
#ifdef COMPAT_MODE
push_shared_string(make_shared_string(res));
#else
{
res = add_slash(res);
push_shared_string(make_shared_string(res));
free(res);
/**MALLOC**/
}
#endif
} else {
push_number(0);
}
break;
}
CASE(F_SNOOP);
{
/* This one takes a variable number of arguments. It returns
* 0 or an object.
*/
#ifdef COMPAT_MODE
struct svalue *a;
if (!command_giver || num_arg == 2)
{
pop_n_elems (num_arg);
push_number (0);
}
a = apply("query_level", command_giver, 0);
if (!a || a->type != T_NUMBER || a->u.number < SNOOP_LEVEL)
{
pop_n_elems(num_arg);
push_number(0);
}
else if (num_arg == 0)
{
set_snoop(command_giver, (struct object *)0);
push_number(0);
}
else
set_snoop(command_giver, sp->u.ob);
#else
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 (new_set_snoop(sp->u.ob, 0))
ob = sp->u.ob;
break;
case 2:
if (new_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, "F_SNOOP");
else
push_number(0);
}
#endif
break;
}
CASE(F_ADD_ACTION);
{
struct svalue *arg = sp - num_arg + 1;
if (num_arg == 3) {
if (arg[2].type != T_NUMBER)
bad_arg(3, instruction);
}
add_action(arg[0].u.string,
num_arg > 1 ? arg[1].u.string : 0,
num_arg > 2 ? arg[2].u.number : 0);
pop_n_elems(num_arg-1);
break;
}
CASE(F_ADD_VERB);
add_verb(sp->u.string, 0);
break;
CASE(F_ADD_XVERB);
add_verb(sp->u.string, 1);
break;
CASE(F_ALLOCATE);
{
struct vector *v;
v = allocate_array(sp->u.number); /* Will have ref count == 1 */
pop_stack();
push_vector(v);
v->ref--;
break;
}
CASE(F_ED);
if (num_arg == 0) {
struct svalue *arg;
char *err_file;
if (command_giver == 0 || command_giver->interactive == 0) {
push_number(0);
break;
}
arg = sapply("query_real_name", command_giver, 0);
if (arg == 0 || arg->type != T_STRING) {
push_number(0);
break;
}
err_file = get_error_file(arg->u.string);
if (err_file == 0) {
push_number(0);
break;
}
ed_start(err_file, 0, 0);
push_number(1);
break;
} 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();
}
break;
CASE(F_CRYPT);
{
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))];
salt[1] = choise[random_number(strlen(choise))];
}
#ifdef sun
res = make_shared_string(_crypt((sp-1)->u.string, salt)); /**MALLOC**/
#else
res = make_shared_string(crypt((sp-1)->u.string, salt)); /**MALLOC**/
#endif
pop_n_elems(2);
push_shared_string(res);
break;
}
#ifdef F_CREATE_WIZARD
CASE(F_CREATE_WIZARD);
{
char *str;
struct svalue *arg = sp - num_arg + 1;
str = create_wizard(arg[0].u.string,
num_arg == 2 ? arg[1].u.string : 0);
pop_n_elems(num_arg);
if (str)
push_string(str, STRING_CONSTANT);
else
push_number(0);
break;
}
#endif
CASE(F_DESTRUCT);
{
int really;
if (num_arg == 2)
{
really = sp->u.number;
pop_stack();
}
else really = 0;
destruct_object(sp, really);
break;
}
CASE(F_RANDOM);
if (sp->u.number <= 0) {
sp->u.number = 0;
break;
}
sp->u.number = random_number(sp->u.number);
break;
CASE(F_SAY);
#if 0
if (num_arg == 1)
say(sp, 0);
else {
say(sp-1, sp->u.ob);
pop_stack();
}
#else
{
static struct {
struct vector v;
struct svalue second_item[1];
} vtmp = { { 2, 1,
#ifdef DEBUG
1,
#endif
(struct wiz_list *)NULL,
{ { T_NUMBER } } }, { { T_OBJECT } }
};
if (num_arg == 1) {
vtmp.v.item[0].type = T_NUMBER; /* this marks the place for the
command_giver
*/
vtmp.v.item[1].type = T_NUMBER; /* will not match any object... */
say(sp, &vtmp.v);
} else {
if ( sp->type == T_POINTER ) {
if (sp->u.vec->ref > 1) {
struct vector *vtmpp =
slice_array(sp->u.vec, 0, sp->u.vec->size-1);
say(sp-1, vtmpp);
free_vector(vtmpp);
} else
say(sp-1, sp->u.vec);
} else {
vtmp.v.item[0].type = T_NUMBER;
vtmp.v.item[1].type = T_OBJECT;
vtmp.v.item[1].u.ob = sp->u.ob;
add_ref(sp->u.ob, "F_SAY");
say(sp-1, &vtmp.v);
}
pop_stack();
}
break;
}
#endif
break;
CASE(F_TELL_ROOM);
{
extern struct vector null_vector;
struct svalue *arg = sp- num_arg + 1;
struct vector *avoid;
if (arg[0].type == T_OBJECT)
ob = arg[0].u.ob;
else {
ob = find_object(arg[0].u.string);
if (ob == 0)
error("Object not found.\n");
}
if (num_arg == 2) {
avoid = &null_vector;
avoid->ref++;
} else {
extern struct vector *order_alist PROT((struct vector *));
struct vector *vtmpp;
static struct vector vtmp = { 1, 1,
#ifdef DEBUG
1,
#endif
(struct wiz_list *)NULL,
{ { T_POINTER } }
};
if (arg[2].type != T_POINTER)
bad_arg(3, instruction);
vtmp.item[0].u.vec = arg[2].u.vec;
if (vtmp.item[0].u.vec->ref > 1) {
vtmp.item[0].u.vec->ref--;
vtmp.item[0].u.vec = slice_array(
vtmp.item[0].u.vec, 0, vtmp.item[0].u.vec->size-1);
}
sp--;
vtmpp = order_alist(&vtmp);
avoid = vtmpp->item[0].u.vec;
vtmpp->item[0].u.vec = vtmp.item[0].u.vec;
free_vector(vtmpp);
}
tell_room(ob, sp, avoid);
free_vector(avoid);
pop_stack();
break;
}
CASE(F_SHOUT);
shout_string(sp->u.string);
break;
CASE(F_WHILE);
fatal("F_WHILE should not appear.\n");
CASE(F_DO);
fatal("F_DO should not appear.\n");
CASE(F_FOR);
fatal("F_FOR should not appear.\n");
CASE(F_SWITCH);
{
extern char* findstring PROT((char*));
unsigned short offset,break_adr;
int d,s,r;
char *l,*end_tab;
static short off_tab[] = { 0*6,1*6,3*6,7*6,15*6,31*6,63*6,127*6,255*6,
511*6,1023*6,2047*6,4095*6,8191*6 };
((char *)&offset)[0] = pc[1];
((char *)&offset)[1] = pc[2];
((char *)&break_adr)[0] = pc[3];
((char *)&break_adr)[1] = pc[4];
*--break_sp = break_adr;
if ( ( i = EXTRACT_UCHAR(pc) >> 4 ) != 0xf ) {
if ( sp->type == T_NUMBER && !sp->u.number ) {
/* special case: uninitalized string */
s = ZERO_AS_STR_CASE_LABEL;
} else if ( sp->type == T_STRING ) {
switch(sp->string_type) {
case STRING_SHARED:
s = (int)sp->u.string;
break;
default:
s = (int)findstring(sp->u.string);
break;
}
} else {
bad_arg(1, F_SWITCH);
}
} else {
if (sp->type != T_NUMBER) bad_arg(1, F_SWITCH);
s = sp->u.number;
i = (int)pc[0] &0xf ;
}
pop_stack();
end_tab = current_prog->program + break_adr;
if ( i >= 14 )
if ( i == 14 ) {
/* fastest switch format : lookup table */
l = current_prog->program + offset;
((char *)&d)[0] = end_tab[-4];
((char *)&d)[1] = end_tab[-3];
((char *)&d)[2] = end_tab[-2];
((char *)&d)[3] = end_tab[-1];
if ( s >= d && l + (s=(s-d)*sizeof(short)) < end_tab - 4 ) {
((char *)&offset)[0] = l[s];
((char *)&offset)[1] = l[s+1];
if (offset) {
pc = current_prog->program + offset;
break;
}
}
/* default */
((char *)&offset)[0] = pc[5];
((char *)&offset)[1] = pc[6];
pc = current_prog->program + offset;
break;
} else
fatal("unsupported switch table format.\n");
l = current_prog->program + offset + off_tab[i];
d = (off_tab[i]+6) >> 1;
if (d == 3) d=0;
for(;;) {
((char *)&r)[0] = l[0];
((char *)&r)[1] = l[1];
((char *)&r)[2] = l[2];
((char *)&r)[3] = l[3];
if (s < r)
if (d < 6) {
if (!d) { /* test for range */
((char *)&offset)[0] = l[-2];
((char *)&offset)[1] = l[-1];
/* F_BREAK is required to be > 1 */
if (offset <= 1) {
((char *)&r)[0] = l[-6];
((char *)&r)[1] = l[-5];
((char *)&r)[2] = l[-4];
((char *)&r)[3] = l[-3];
if (s >= r) {
/* s is in the range */
if (!offset) {
/* range with lookup table */
((char *)&offset)[0] = l[4];
((char *)&offset)[1] = l[5];
l = current_prog->program + offset +
(s-r) * sizeof(short);
((char *)&offset)[0] = l[0];
((char *)&offset)[1] = l[1];
break;
}
((char *)&offset)[0] = l[4];
((char *)&offset)[1] = l[5];
break;
}
}
/* use default address */
((char *)&offset)[0] = pc[5];
((char *)&offset)[1] = pc[6];
break;
} /* !d */
d = 0;
} else {
/* d >= 6 */
l -= d;
d >>= 1;
}
else if (s > r) {
if (d < 6) {
if (!d) { /* test for range */
((char *)&offset)[0] = l[4];
((char *)&offset)[1] = l[5];
if (offset <= 1) {
((char *)&r)[0] = l[6];
((char *)&r)[1] = l[7];
((char *)&r)[2] = l[8];
((char *)&r)[3] = l[9];
if (s <= r) {
/* s is in the range */
if (!offset) {
/* range with lookup table */
((char *)&offset)[0] = l[10];
((char *)&offset)[1] = l[11];
l = current_prog->program + offset +
(s-r) * sizeof(short);
((char *)&offset)[0] = l[0];
((char *)&offset)[1] = l[1];
break;
}
((char *)&offset)[0] = l[10];
((char *)&offset)[1] = l[11];
break;
}
}
/* use default address */
((char *)&offset)[0] = pc[5];
((char *)&offset)[1] = pc[6];
break;
} /* !d */
d = 0;
} else {
/* d >= 6 */
l += d;
while (l >= end_tab) {
d >>= 1;
if (d <= 3) {
if (!d) break;
d = 0;
}
l -= d;
}
d >>= 1;
}
} else {
/* s == r */
((char *)&offset)[0] = l[4];
((char *)&offset)[1] = l[5];
if ( !l[-2] && !l[-1] ) {
/* end of range with lookup table */
((char *)&r)[0] = l[-6];
((char *)&r)[1] = l[-5];
((char *)&r)[2] = l[-4];
((char *)&r)[3] = l[-3];
l = current_prog->program + offset + (s-r)*sizeof(short);
((char *)&offset)[0] = l[0];
((char *)&offset)[1] = l[1];
}
if (offset <= 1) {
if (!offset) {
/* start of range with lookup table */
((char *)&offset)[0] = l[10];
((char *)&offset)[1] = l[11];
l = current_prog->program + offset;
((char *)&offset)[0] = l[0];
((char *)&offset)[1] = l[1];
} else {
((char *)&offset)[0] = l[10];
((char *)&offset)[1] = l[11];
}
}
break;
}
}
pc = current_prog->program + offset;
break;
}
CASE(F_BREAK);
{
pc = current_prog->program + *break_sp++;
break;
}
CASE(F_SUBSCRIPT);
fatal("F_SUBSCRIPT should not appear.\n");
CASE(F_STRLEN);
i = strlen(sp->u.string);
pop_stack();
push_number(i);
break;
CASE(F_SIZEOF);
if (sp->type == T_NUMBER)
i = 0;
else
i = sp->u.vec->size;
pop_stack();
push_number(i);
break;
CASE(F_LOWER_CASE);
{
char *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_shared_string(make_shared_string(str)); /*MALLOC*/
free(str);
break;
}
CASE(F_SET_HEART_BEAT);
i = set_heart_beat(current_object, sp->u.number);
sp->u.number = i;
break;
CASE(F_CAPITALIZE);
if (islower(sp->u.string[0])) {
char *str;
str = string_copy(sp->u.string);
str[0] += 'A' - 'a';
pop_stack();
push_shared_string(make_shared_string(str));
free(str);/*MALLOC*/
}
break;
CASE(F_PROCESS_STRING);
{
extern char *process_string PROT((char *));
char *str;
str = process_string(sp->u.string);
if (str != sp->u.string) {
pop_stack();
push_shared_string(make_shared_string(str));
}
break;
}
CASE(F_COMMAND);
{
struct svalue *arg = sp - num_arg + 1;
if (num_arg == 1)
i = command_for_object(arg[0].u.string, 0);
else
#ifdef COMPAT_MODE
i = command_for_object(arg[0].u.string, arg[1].u.ob);
#else
error("Too many arguments to command().\n");
#endif
pop_n_elems(num_arg);
push_number(i);
break;
}
CASE(F_GET_DIR);
{
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);
break;
}
CASE(F_LS);
if (sp->type == T_NUMBER)
list_files(NULL);
else
list_files(sp->u.string);
break;
CASE(F_RM);
i = remove_file(sp->u.string);
pop_stack();
push_number(i);
break;
CASE(F_CAT);
{
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, instruction);
len = arg[2].u.number;
}
i = print_file(arg[0].u.string, start, len);
pop_n_elems(num_arg);
push_number(i);
break;
}
CASE(F_MKDIR);
{
char *path;
#ifdef COMPAT_MODE
path = check_file_name(sp->u.string, 1);
#else
path = check_valid_path(sp->u.string, current_object->eff_user,
"mkdir", 1);
#endif
/* pop_stack(); marion - what the heck ??? */
if (path == 0 || mkdir(path, 0770) == -1)
assign_svalue(sp, &const0);
else
assign_svalue(sp, &const1);
break;
}
CASE(F_RMDIR);
{
char *path;
#ifdef COMPAT_MODE
path = check_file_name(sp->u.string, 1);
#else
path = check_valid_path(sp->u.string, current_object->eff_user,
"rmdir", 1);
#endif
/* pop_stack(); */
if (path == 0 || rmdir(path) == -1)
assign_svalue(sp, &const0);
else
assign_svalue(sp, &const1);
break;
}
CASE(F_INPUT_TO);
{
struct svalue *arg = sp - num_arg + 1;
int flag = 1;
if (num_arg == 1 || sp->type == T_NUMBER && sp->u.number == 0)
flag = 0;
i = input_to(arg[0].u.string, flag);
pop_n_elems(num_arg);
push_number(i);
break;
}
CASE(F_SET_LIVING_NAME);
set_living_name(current_object, sp->u.string);
break;
CASE(F_PARSE_COMMAND);
{
struct svalue *arg;
num_arg = EXTRACT_UCHAR(pc);
pc++;
arg = sp - num_arg + 1;
if (arg[0].type != T_STRING)
bad_arg(1, F_PARSE_COMMAND);
if (arg[1].type != T_OBJECT && arg[1].type != T_POINTER)
bad_arg(2, F_PARSE_COMMAND);
if (arg[2].type != T_STRING)
bad_arg(3, F_PARSE_COMMAND);
if (arg[1].type == T_POINTER)
check_for_destr(arg[1].u.vec);
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 */
break;
}
CASE(F_SSCANF);
num_arg = EXTRACT_UCHAR(pc);
pc++;
i = inter_sscanf(num_arg);
pop_n_elems(num_arg);
push_number(i);
break;
CASE(F_ENABLE_COMMANDS);
enable_commands(1);
push_number(1);
break;
CASE(F_DISABLE_COMMANDS);
enable_commands(0);
push_number(0);
break;
CASE(F_PRESENT);
{
struct svalue *arg = sp - num_arg + 1;
ob = object_present(arg, num_arg == 1 ? 0 : arg[1].u.ob);
pop_n_elems(num_arg);
if (ob)
push_object(ob, "F_PRESENT");
else
push_number(0);
}
break;
CASE(F_SET_LIGHT);
{
struct object *o1;
add_light(current_object, sp->u.number);
o1 = current_object;
while(o1->super)
o1 = o1->super;
sp->u.number = o1->total_light;
break;
}
CASE(F_CONST0);
push_number(0);
break;
CASE(F_CONST1);
push_number(1);
break;
CASE(F_NUMBER);
((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);
break;
CASE(F_ASSIGN);
#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();
break;
CASE(F_CTIME);
{
char *cp, *a;
int n = current_time;
if (num_arg) { n = sp->u.number; pop_stack(); }
cp = string_copy(time_string(n));
/* Now strip the newline. */
a = strchr(cp, '\n');
if (a)
*a = '\0';
push_shared_string(make_shared_string(cp));
free(cp);/*MALLOC*/
break;
}
CASE(F_ADD_EQ);
if (sp[-1].type != T_LVALUE)
bad_arg(1, F_ADD_EQ);
argp = sp[-1].u.lvalue;
switch(argp->type)
{
case T_POINTER:
{
struct vector *v;
if (sp->type != T_POINTER)
v = append_vector(argp->u.vec, sp);
else
v = add_array(argp->u.vec, sp->u.vec);
pop_n_elems(2);
push_vector(v);
v->ref--;
break;
}
case T_STRING:
{
char *new_str;
switch(sp->type)
{
case T_STRING: {
new_str=xalloc(strlen(argp->u.string)+
strlen(sp->u.string)+1);
strcpy(new_str, argp->u.string);
strcat(new_str, sp->u.string);
pop_n_elems(2);
push_shared_string(make_shared_string(new_str));
free(new_str);/**MALLOC**/
break;
}
case 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_shared_string(make_shared_string(new_str));
free(new_str);/**MALLOC**/
break;
}
case T_POINTER: {
struct vector *v;
v = prepend_vector(sp->u.vec, argp);
pop_n_elems(2);
push_vector(v);
v->ref--;
break;
}
default: error("Bad type RHS to +=\n");
}
}
break;
case T_NUMBER: {
switch(sp->type)
{
case T_NUMBER:
i = argp->u.number + sp->u.number;
pop_n_elems(2);
push_number(i);
break;
case T_POINTER:
{
struct vector *v;
v = prepend_vector(sp->u.vec, argp);
pop_n_elems(2);
push_vector(v); /* This will make ref count == 2 */
v->ref--;
break;
}
case T_STRING:
{
char buff[20], *new_str;
sprintf(buff, "%d", argp->u.number);
new_str=xalloc(strlen(sp->u.string) + strlen(buff) + 1);
strcpy(new_str, buff);
strcat(new_str, sp->u.string);
pop_n_elems(2);
push_shared_string(make_shared_string(new_str));
free(new_str);/**MALLOC**/
break;
}
default:
error("Bad type number to rhs +=.\n");
}
}
break;
default:
error("Bad type to lhs +=");
}
assign_svalue(argp, sp);
break;
CASE(F_SUB_EQ);
if (sp[-1].type != T_LVALUE)
bad_arg(1, F_SUB_EQ);
argp = sp[-1].u.lvalue;
switch(argp->type)
{
case T_NUMBER:
if (sp->type != T_NUMBER)
error("Bad right type to -=\n");
i = argp->u.number - sp->u.number;
pop_n_elems(2);
push_number(i);
break;
case T_POINTER:
#ifdef COMPAT_MODE
{
struct vector *v;
v = subtract_array(argp->u.vec, sp);
pop_n_elems(2);
if (v) { push_vector(v); v->ref--; }
else push_number(0);
break;
}
#else
{
struct vector *subtract_array PROT((struct vector*,struct vector*));
struct vector *v;
if (sp->type != T_POINTER)
error("Bad right type to -=");
v = sp->u.vec;
if (v->ref > 1) {
v = slice_array(v, 0, v->size-1 );
v->ref--;
}
sp--;
v = subtract_array(argp->u.vec, v);
free_vector(argp->u.vec);
argp->u.vec = v;
break;
}
#endif
default: error("Bad lhs to -=\n");
}
assign_svalue(argp, sp);
break;
CASE(F_MULT_EQ);
if (sp[-1].type != T_LVALUE)
bad_arg(1, F_MULT_EQ);
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);
break;
CASE(F_AND_EQ);
if (sp[-1].type != T_LVALUE)
bad_arg(1, F_AND_EQ);
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);
break;
CASE(F_OR_EQ);
if (sp[-1].type != T_LVALUE)
bad_arg(1, F_OR_EQ);
argp = sp[-1].u.lvalue;
if (sp[-1].type != T_LVALUE)
bad_arg(1, F_OR_EQ);
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);
break;
CASE(F_XOR_EQ);
if (sp[-1].type != T_LVALUE)
bad_arg(1, F_XOR_EQ);
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);
break;
CASE(F_LSH_EQ);
if (sp[-1].type != T_LVALUE)
bad_arg(1, F_LSH_EQ);
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);
break;
CASE(F_RSH_EQ);
if (sp[-1].type != T_LVALUE)
bad_arg(1, F_RSH_EQ);
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);
break;
#ifdef F_COMBINE_FREE_LIST
CASE(F_COMBINE_FREE_LIST);
#ifdef MALLOC_malloc
push_number(resort_free_list());
#else
push_number(0);
#endif
break;
#endif
CASE(F_DIV_EQ);
if (sp[-1].type != T_LVALUE)
bad_arg(1, F_DIV_EQ);
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);
break;
CASE(F_MOD_EQ);
if (sp[-1].type != T_LVALUE)
bad_arg(1, F_MOD_EQ);
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);
break;
CASE(F_STRING);
{
unsigned short string_number;
((char *)&string_number)[0] = pc[0];
((char *)&string_number)[1] = pc[1];
pc += 2;
push_string(current_prog->strings[string_number],
STRING_CONSTANT);
break;
}
#ifdef F_RUSAGE
CASE(F_RUSAGE);
{
char buff[500];
struct rusage rus;
long utime, stime;
int maxrss;
if (getrusage(RUSAGE_SELF, &rus) < 0)
buff[0] = 0;
else {
utime = rus.ru_utime.tv_sec * 1000 + rus.ru_utime.tv_usec / 1000;
stime = rus.ru_stime.tv_sec * 1000 + rus.ru_stime.tv_usec / 1000;
maxrss = rus.ru_maxrss;
#ifdef sun
maxrss *= getpagesize() / 1024;
#endif
sprintf(buff, "%ld %ld %d %d %d %d %d %d %d %d %d %d %d %d %d %d",
utime, stime, maxrss, rus.ru_ixrss, rus.ru_idrss,
rus.ru_isrss, rus.ru_minflt, rus.ru_majflt, rus.ru_nswap,
rus.ru_inblock, rus.ru_oublock, rus.ru_msgsnd,
rus.ru_msgrcv, rus.ru_nsignals, rus.ru_nvcsw,
rus.ru_nivcsw);
}
push_string(buff, STRING_SHARED);
break;
}
#endif
CASE(F_CINDENT);
{
char *path;
#ifndef COMPAT_MODE
path = check_valid_path(sp->u.string, current_object->eff_user, "cindent", 1);
#else
path = check_file_name(sp->u.string, 1);
#endif
pop_stack();
if (path) {
if (indent_program(path)) {
push_number(1);
break;
}
} else {
add_message("Illegal attempt to indent\n");
}
push_number(0);
break;
}
CASE(F_DESCRIBE);
{
char *str;
int live;
if (num_arg < 3) live = 0;
else {
if (sp->type != T_NUMBER) bad_arg (3, F_DESCRIBE);
live = sp->u.number;
pop_stack ();
}
str = describe_items(sp-1, sp->u.string, live);
pop_n_elems(2);
if (str) push_shared_string (make_shared_string(str));
else push_number(0);
break;
}
CASE(F_UNIQUE_ARRAY); {
extern struct vector
*make_unique PROT((struct vector *arr,char *func,
struct svalue *skipnum));
struct vector *res;
if (num_arg < 3)
res = make_unique((sp-1)->u.vec, sp->u.string, &const0);
else {
res = make_unique ((sp-2)->u.vec, (sp-1)->u.string, sp);
pop_stack ();
}
pop_n_elems(2);
if (res) { push_vector(res); res->ref--; }
else push_number(0);
break;
}
CASE(F_VERSION); {
char buff[12];
sprintf(buff, "%5.5s%2d-DR", GAME_VERSION, PATCH_LEVEL);
push_string(buff, STRING_SHARED);
break;
}
CASE(F_MAP_ARRAY); {
struct vector *res;
struct svalue *arg;
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_ARRAY);
if (arg[0].type == T_POINTER)
res = map_array (arg[0].u.vec, arg[1].u.string, ob,
num_arg > 3 ? sp : (struct svalue *)0);
else
res = 0;
pop_n_elems (num_arg);
if (res) { push_vector (res); sp->u.vec->ref--; }
else push_number (0);
break;
}
/* ADDED -- 5-91 Raistlin -- Americanized standard LP */
/*
CASE(F_REMOVE_LIVING_NAME)
remove_one_living_name(sp->u.string, current_object);
break;
*/
CASE(F_ROOT);
{
struct object *obj = current_object;
int i;
if (num_arg == 1)
obj = sp->u.ob;
i = (strchr(obj->name, '#') == NULL);
pop_n_elems(num_arg);
push_number(i);
break;
}
CASE(F_DELETEA);
{
struct svalue *arg = sp - num_arg + 1;
struct vector *ret;
int j = arg[1].u.number;
if (num_arg == 3)
{
if (arg[2].type != T_NUMBER)
bad_arg(3, F_DELETEA);
j = arg[2].u.number;
}
ret = delete_elements(arg[0].u.vec, arg[1].u.number, j);
pop_n_elems(num_arg);
if (ret) { push_vector(ret); ret->ref--; }
else push_number(0);
break;
}
CASE(F_EXISTS);
{
struct stat st;
int offset = 0;
char *file = sp->u.string;
if (*file == '/') offset = 1;
pop_stack();
if (stat(file+offset, &st) != -1)
push_number(1);
else push_number(0);
break;
}
CASE(F_PAD);
{
struct svalue *arg = sp-num_arg+1;
char *new, *str, numbuf[20];
int len = arg[1].u.number, strl;
int right_justify = 0, padchar = ' ';
if (num_arg == 3)
{
if (arg[2].type != T_NUMBER)
bad_arg(3, F_PAD);
padchar = arg[2].u.number;
}
if (arg[0].type == T_NUMBER)
{
sprintf(numbuf, "%d", arg[0].u.number);
str = numbuf;
}
else
str = arg[0].u.string;
if (len < 0)
{
len = -len;
right_justify = 1;
}
if (len == 0)
{
/* Nothing to pad!! */
pop_n_elems(num_arg);
push_constant_string("");
break;
}
strl = strlen(str);
if (strl > len) strl = len;
new = xalloc(len+1);
if (right_justify)
{
memset(new, padchar, len);
if (strl)
strncpy(new+len-strl, str, strl);
}
else
{
if (strl)
strncpy(new, str, len);
if (strl < len)
memset(new+strl, padchar, len-strl);
}
new[len] = '\0';
pop_n_elems(num_arg);
push_shared_string(make_shared_string(new));
free(new);
break;
}
CASE(F_REMOTE_COMMAND);
push_number(remote_command);
break;
CASE(F_IN_EDITOR);
{
struct object *a = command_giver;
if (num_arg==1)
{
a = sp->u.ob;
pop_stack();
}
if (a && a->interactive && a->interactive->ed_buffer)
push_number(1);
else
push_number(0);
break;
}
CASE(F_GRAB_FILE);
{
struct svalue *arg = sp - num_arg + 1;
int start=0, len=0;
struct vector *r;
if (num_arg > 1)
start = arg[1].u.number;
if (num_arg == 3)
len = arg[2].u.number;
r = grab_file(arg[0].u.string, start, len);
pop_n_elems(num_arg);
if (r) { push_vector(r); sp->u.vec->ref--; }
else push_number(0);
break;
}
CASE(F_FILES);
{
struct vector *r;
r = get_files(sp->u.string);
pop_stack();
if (r) { push_vector(r); sp->u.vec->ref--; }
else push_number(0);
break;
}
#ifdef F_POSSESSIVE
CASE(F_POSSESSIVE);
{
struct object *obj = command_giver;
if (num_arg == 1) obj = sp->u.ob;
pop_n_elems(num_arg);
if (!obj || obj->flags & O_DESTRUCTED)
push_number(0);
else
{
struct svalue *r = sapply("query_gender", obj, 0);
if (!r || r->type != T_STRING)
push_constant_string("its");
else
{
if (!strcmp(r->u.string, "male"))
push_constant_string("his");
else if (!strcmp(r->u.string, "female"))
push_constant_string("her");
else push_constant_string("its");
}
}
break;
}
#endif
#ifdef F_OBJECTIVE
CASE(F_OBJECTIVE);
{
struct object *obj;
if (num_arg == 1) obj = sp->u.ob;
else obj = command_giver;
pop_n_elems(num_arg);
if (!obj || obj->flags & O_DESTRUCTED)
push_number(0);
else
{
struct svalue *r = sapply("query_gender", obj, 0);
if (!r || r->type != T_STRING)
push_constant_string("it");
else
{
if (!strcmp(r->u.string, "male"))
push_constant_string("him");
else if (!strcmp(r->u.string, "female"))
push_constant_string("her");
else push_constant_string("it");
}
}
break;
}
#endif
#ifdef F_SUBJECTIVE
CASE(F_SUBJECTIVE);
{
struct object *obj;
if (num_arg == 1) obj = sp->u.ob;
else obj = command_giver;
pop_n_elems(num_arg);
if (!obj || obj->flags & O_DESTRUCTED)
push_number(0);
else
{
struct svalue *r = sapply("query_gender", obj, 0);
if (!r || r->type != T_STRING)
push_constant_string("it");
else
{
if (!strcmp(r->u.string, "male"))
push_constant_string("he");
else if (!strcmp(r->u.string, "female"))
push_constant_string("she");
else push_constant_string("it");
}
}
break;
}
#endif
CASE(F_REMOVE_INTERACTIVE)
remove_interactive_player(sp->u.ob);
break;
CASE(F_CP)
{
int i;
i = copy_file((sp-1)->u.string, sp->u.string);
pop_n_elems(2);
push_number(i);
break;
}
CASE(F_RENAME);
{
int i;
i = rename_file((sp-1)->u.string, sp->u.string);
pop_n_elems(2);
push_number(i);
break;
}
CASE(F_TYPEOF);
{
char *ret;
int t;
t = sp->type;
pop_stack();
switch(t) {
case T_OBJECT:
ret = "object";
break;
case T_NUMBER:
ret = "int";
break;
case T_POINTER:
ret = "vector";
break;
case T_STRING:
ret = "string";
break;
default: ret = "";
}
push_constant_string(ret);
break;
}
CASE(F_CALLER);
{
struct object *o = caller();
if (o)
push_object(o, "F_CALLER");
else push_number(0);
break;
}
CASE(F_READ_FILE);
{
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, instruction);
len = arg[2].u.number;
}
/* back compatibility with American versions */
#ifdef COMPAT_MODE
else if (num_arg == 2)
len = 1;
#endif
str = read_file(arg[0].u.string, start, len);
pop_n_elems(num_arg);
if (str == 0)
push_number(0);
else {
push_string(str, STRING_MALLOC);
free(str);
}
break;
}
CASE(F_INTERACTIVE)
{
int ret;
if (sp->u.ob->flags&O_DESTRUCTED)
ret = 0;
else if (sp->u.ob->interactive)
ret = 1;
else ret = 0;
pop_stack();
push_number(ret);
break;
}
CASE(F_FIND_CALL_OUT);
{
int ret;
ret = find_call_out(current_object, sp->u.string);
pop_stack();
push_number(ret);
break;
}
CASE(F_MEMBER_ARRAY)
{
int ret;
ret = search_array(sp, sp-1, 0);
pop_n_elems(2);
push_number(ret);
break;
}
CASE(F_SEARCHA);
case F_INDEX:
{
int ret;
if (num_arg == 3)
{
if (sp->type != T_NUMBER)
error("Bad argument number 3 to searcha.\n");
ret = search_array((sp-2), (sp-1), sp->u.number);
}
else
ret = search_array((sp-1), sp, 0);
pop_n_elems(num_arg);
push_number(ret);
break;
}
CASE(F_REALLOCATE);
{
struct vector *v;
v = reallocate_array((sp-1)->u.vec, sp->u.number);
pop_n_elems(2);
push_vector(v);
sp->u.vec->ref--;
}
break;
CASE(F_MEMUSED); /* Render 2/6/91 */
push_number((int)memused());
break;
/*
** modified 3/30/91 Raistlin add_attribute, query_attribute,
** delete_attribute, get_attribute, add_prot_attribute, format, and print
*/
CASE(F_GET_ATTRIBUTE); {
/* arguments get_attribute(string, object)
** returns value associated with string
** get_attribute(object)
** returns an array of all attributes associated with object
** low level split into get_attr_vector and get_attribute
*/
struct vector *v;
int ret;
char *attr = NULL;
struct object *o = current_object;
if (num_arg == 0 || (num_arg == 1 && sp->type == T_OBJECT &&
(o = sp->u.ob))) /* yes, I want a single equal here */
{
v = get_attr_vector(o);
pop_n_elems(num_arg);
push_vector(v);
sp->u.vec->ref--;
break;
}
else
{
if (num_arg == 2)
{
attr = (sp-1)->u.string;
o = sp->u.ob;
}
else attr = sp->u.string;
ret = get_attribute(attr, o);
pop_n_elems(num_arg);
push_number(ret);
}
}
break;
CASE(F_DELETE_ATTRIBUTE);
{
struct object *o;
char *attr;
if (num_arg == 2)
{
o = sp->u.ob;
attr = (sp-1)->u.string;
}
else
{
o = current_object;
attr = sp->u.string;
}
delete_attribute(attr, o);
pop_n_elems(num_arg-1); /* delete is void, leave a return value */
}
break;
case F_ADD_PROT_ATTRIBUTE:
CASE(F_ADD_ATTRIBUTE);
{
struct object *t=current_object;
char *attr;
int val = 1;
char *c=(instruction==F_ADD_ATTRIBUTE?
"add_attribute":"add_prot_attribute");
if (num_arg == 3)
{
if (sp->type != T_OBJECT)
error("Bad type argument 3 to %s()\n", c);
if ((sp-1)->type != T_NUMBER)
error("Bad type argument 2 to %s()\n", c);
t = sp->u.ob;
val = (sp-1)->u.number;
attr = (sp-2)->u.string;
}
else if (num_arg == 2)
{
if (sp->type == T_OBJECT)
t = sp->u.ob;
else val = sp->u.number;
attr = (sp-1)->u.string;
}
else attr = sp->u.string;
add_attribute(attr, t, val, instruction == F_ADD_PROT_ATTRIBUTE);
pop_n_elems(num_arg-1); /* add is void */
}
break;
CASE(F_QUERY_ATTRIBUTE);
{
char *attr;
int ret;
struct object *o=current_object;
if (num_arg == 2)
{
attr = (sp-1)->u.string;
o = sp->u.ob;
}
else attr = sp->u.string;
ret = query_attribute(attr, o);
pop_n_elems(num_arg);
push_number(ret);
}
break;
case F_FORMAT:
{
char *c, *txt;
int width=0;
if (num_arg == 2)
{
width = sp->u.number;
txt = (sp-1)->u.string;
}
else txt = sp->u.string;
/*
if (width > TEXT_WIDTH)
error("Invalid size for argument 2 to text()\n");
*/
c = textformat(txt, width);
if (!c)
error("Error formatting arg 1 to format()\n");
pop_n_elems(num_arg);
push_string(c, STRING_SHARED);
}
break;
CASE(F_PRINT);
{
char *txt;
int width = 0;
char *c;
struct svalue v;
if (num_arg == 2)
{
txt = (sp-1)->u.string;
width = sp->u.number;
}
else
txt = sp->u.string;
if (width > TEXT_WIDTH)
{
error("Invalid size for argument 2 to print()\n");
exit(1);
}
c = textformat(txt, width);
if (!c)
{
error("Error formatting arg 1 to print()\n");
exit(1);
}
v.u.string = c;
v.type = T_STRING;
v.string_type = STRING_CONSTANT;
(void)do_write(&v);
pop_n_elems(num_arg-1); /* void */
break;
}
CASE(F_SORT_ARRAY); {
extern struct vector *sort_array
PROT((struct vector*,char *,struct object *));
struct vector *res;
struct svalue *arg;
arg = sp - 2; 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_SORT_ARRAY);
if (arg[0].type == T_POINTER)
/* sort_array already takes care of destructed objects */
res = sort_array (
slice_array(arg[0].u.vec, 0, arg[0].u.vec->size-1),
arg[1].u.string, ob);
else
res = 0;
pop_n_elems (3);
sp++;
if (res) {
sp->type = T_POINTER;
sp->u.vec = res;
}
else *sp = const0;
break;
}
CASE(F_ORDER_ALIST);
{
extern struct vector *order_alist PROT((struct vector *));
struct svalue *args;
struct vector *list;
int listsize,keynum;
if (num_arg == 1 && sp->u.vec->size
&& sp->u.vec->item[0].type == T_POINTER) {
args = sp->u.vec->item;
listsize = sp->u.vec->size;
} else {
args = sp-num_arg+1;
listsize = num_arg;
}
keynum = args[0].u.vec->size;
list = allocate_array(listsize);
for (i=0; i<listsize; i++) {
if (args[i].type != T_POINTER
|| args[i].u.vec->size != keynum) {
free_vector(list);
error("bad data array %d in call to order_alist",i);
}
list->item[i].type = T_POINTER;
list->item[i].u.vec = slice_array(args[i].u.vec,0,keynum-1);
}
pop_n_elems(num_arg);
sp++;
sp->type = T_POINTER;
sp->u.vec = order_alist(list);
free_vector(list);
break;
}
CASE(F_INSERT_ALIST)
{
/* When the key list of an alist contains destructed objects
it is better not to free them till the next reordering by
order_alist to retain the alist property.
*/
extern struct svalue *insert_alist
PROT((struct svalue *key,struct svalue *key_data,
struct vector *list));
struct vector *list;
int listsize,keynum;
struct svalue *key,*key_data,*ret;
static struct vector tempvec = { 1,1, };
if (sp->type != T_POINTER)
bad_arg(num_arg,F_INSERT_ALIST);
if ( !(listsize = sp->u.vec->size) ||
sp->u.vec->item[0].type != T_POINTER ) {
list = &tempvec;
assign_svalue_no_free(list->item,sp);
listsize = 1;
} else
list = sp->u.vec;
keynum = list->item[0].u.vec->size;
for (i=1; i<listsize; i++) {
if (list->item[i].type != T_POINTER
||list->item[i].u.vec->size != keynum)
bad_arg(num_arg,F_INSERT_ALIST);
}
if (num_arg == 2) {
if (sp[-1].type != T_POINTER) {
key_data = (struct svalue*)NULL;
key = sp-1;
} else {
if (sp[-1].u.vec->size != listsize)
bad_arg(1,F_INSERT_ALIST);
key_data = key = sp[-1].u.vec->item;
}
} else {
if (num_arg - 1 != listsize)
bad_arg(1,F_INSERT_ALIST);
key_data = key = sp-num_arg+1;
}
ret = insert_alist(key,key_data,list);
pop_n_elems(num_arg);
sp++;
*sp = *ret;
break;
}
CASE(F_ASSOC);
{
/* When the key list of an alist contains destructed objects
it is better not to free them till the next reordering by
order_alist to retain the alist property.
*/
int assoc PROT((struct svalue *key, struct vector *keys));
struct svalue *args = sp -num_arg +1;
struct vector *keys,*data;
struct svalue *fail_val;
int ix;
if ( !args[1].u.vec->size ||
args[1].u.vec->item[0].type != T_POINTER ) {
keys = args[1].u.vec;
if (num_arg == 2) {
data = (struct vector *)NULL;
} else {
if (args[2].type != T_POINTER ||
args[2].u.vec->size != keys->size) {
bad_arg(3,F_ASSOC);
}
data = args[2].u.vec;
}
if (num_arg == 4) {
fail_val = &args[3];
} else {
fail_val = &const0;
}
} else {
keys = args[1].u.vec->item[0].u.vec;
if (args[1].u.vec->size > 1) {
if (args[1].u.vec->item[1].type != T_POINTER ||
args[1].u.vec->item[1].u.vec->size != keys->size) {
bad_arg(2,F_ASSOC);
}
data = args[1].u.vec->item[1].u.vec;
} else {
data = (struct vector *)NULL;
}
if (num_arg == 3) fail_val = &args[2];
else if (num_arg == 2) fail_val = &const0;
else {
error ("Too many args to efun assoc().\n");
}
}
ix = assoc(&args[0],keys);
if (data == (struct vector *)NULL) {
pop_n_elems(num_arg);
push_number(ix);
} else {
assign_svalue(args, ix==-1 ? fail_val : &data->item[ix]);
pop_n_elems(num_arg-1);
}
break;
}
CASE(F_INTERSECT_ALIST);
{
extern struct vector *intersect_alist
PROT((struct vector *, struct vector *));
struct vector *tmp = intersect_alist( (sp-1)->u.vec, sp->u.vec );
pop_stack();
free_vector(sp->u.vec);
sp->u.vec = tmp;
}
CASE(F_DEBUG_INFO);
{
struct svalue *arg = sp-num_arg+1;
struct svalue res;
switch ( arg[0].u.number ) {
case 0:
{
int flags;
struct object *obj2;
if (num_arg != 2)
error("bad number of arguments to debug_info");
if ( arg[1].type != T_OBJECT)
bad_arg(1,instruction);
ob = arg[1].u.ob;
flags = ob->flags;
add_message("O_HEART_BEAT : %s\n",
flags&O_HEART_BEAT ?"TRUE":"FALSE");
add_message("O_IS_WIZARD : %s\n",
flags&O_IS_WIZARD ?"TRUE":"FALSE");
add_message("O_ENABLE_COMMANDS : %s\n",
flags&O_ENABLE_COMMANDS ?"TRUE":"FALSE");
add_message("O_CLONE : %s\n",
flags&O_CLONE ?"TRUE":"FALSE");
add_message("O_DESTRUCTED : %s\n",
flags&O_DESTRUCTED ?"TRUE":"FALSE");
add_message("O_SWAPPED : %s\n",
flags&O_SWAPPED ?"TRUE":"FALSE");
add_message("O_ONCE_INTERACTIVE: %s\n",
flags&O_ONCE_INTERACTIVE?"TRUE":"FALSE");
add_message("O_APPROVED : %s\n",
flags&O_APPROVED ?"TRUE":"FALSE");
add_message("O_RESET_STATE : %s\n",
flags&O_RESET_STATE ?"TRUE":"FALSE");
add_message("O_WILL_CLEAN_UP : %s\n",
flags&O_WILL_CLEAN_UP ?"TRUE":"FALSE");
add_message("total light : %d\n", ob->total_light);
add_message("next_reset : %d\n", ob->next_reset);
add_message("time_of_ref : %d\n", ob->time_of_ref);
add_message("ref : %d\n", ob->ref);
#ifdef DEBUG
add_message("extra_ref : %d\n", ob->extra_ref);
#endif
add_message("swap_num : %ld\n", ob->swap_num);
add_message("name : '%s'\n", ob->name);
add_message("next_all : OBJ(%s)\n",
ob->next_all?ob->next_all->name:"NULL");
if (obj_list == ob) add_message(
"This object is the head of the object list.\n");
for (obj2=obj_list,i=1; obj2; obj2=obj2->next_all,i++)
if (obj2->next_all == ob) {
add_message(
"Previous object in object list: OBJ(%s)\n",
obj2->name);
add_message("position in object list:%d\n",i);
}
assign_svalue_no_free(&res,&const0);
break;
}
case 1: {
if (num_arg != 2)
error("bad number of arguments to debug_info");
if ( arg[1].type != T_OBJECT)
bad_arg(1,instruction);
ob = arg[1].u.ob;
add_message("program ref's %d\n", ob->prog->ref);
add_message("Name %s\n", ob->prog->name);
add_message("program size %d\n",
ob->prog->program_size);
add_message("num func's %d (%d) \n", ob->prog->num_functions
,ob->prog->num_functions * sizeof(struct function));
add_message("num strings %d\n", ob->prog->num_strings);
add_message("num vars %d (%d)\n", ob->prog->num_variables
,ob->prog->num_variables * sizeof(struct variable));
add_message("num inherits %d (%d)\n", ob->prog->num_inherited
,ob->prog->num_inherited * sizeof(struct inherit));
add_message("total size %d\n", ob->prog->total_size);
assign_svalue_no_free(&res,&const0);
break;
}
default: bad_arg(1,instruction);
}
pop_n_elems(num_arg);
sp++;
*sp=res;
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;
}
/*
* 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 */
static int apply_low(fun, ob, num_arg)
char *fun;
struct object *ob;
int num_arg;
{
static int cache_id[0x40] = {
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 };
static char *cache_name[0x40];
static struct function *cache_pr[0x40];
static struct function *cache_pr_inherited[0x40];
static struct program *cache_progp[0x40];
static int cache_function_index_offset[0x40];
static int cache_variable_index_offset[0x40];
struct function *pr;
struct program *progp;
extern int num_error;
struct control_stack *save_csp;
int ix;
ob->time_of_ref = current_time; /* Used by the swapper */
#ifdef COMPAT_MODE
if (ob->next_reset < current_time)
{
struct object *s = command_giver;
int save = eval_cost;
/* if it's past due, reset immediately, else just mark it to be reset */
eval_cost = 0;
reset_object(ob, 1);
eval_cost = save;
command_giver = s;
}
else
#endif
ob->flags &= ~O_RESET_STATE;
#ifdef DEBUG
strncpy(debug_apply_fun, fun, sizeof debug_apply_fun);
debug_apply_fun[sizeof debug_apply_fun - 1] = '\0';
#endif
if (num_error > 0)
goto failure;
if (fun[0] == ':')
error("Illegal function call\n");
/*
* If there is a chain of objects shadowing, start with the first
* of these.
*/
while (ob->shadowed && ob->shadowed != current_object)
ob = ob->shadowed;
retry_for_shadow:
if (ob->flags & O_SWAPPED)
load_ob_from_swap(ob);
progp = ob->prog;
#ifdef DEBUG
if (ob->flags & O_DESTRUCTED)
fatal("apply() on destructed object\n");
#endif
ix = ( progp->id_number ^ (int)fun ^ ( (int)fun >> 6 ) ) & 0x3f;
if (cache_id[ix] == progp->id_number && !strcmp(cache_name[ix], fun) ) {
/* We have found a matching entry in the cache. The pointer to
the function name has to match, not only the contents.
This is because hashing the string in order to get a cache index
would be much more costly than hashing it's pointer.
If cache access would be costly, the cache would be useless.
*/
if (cache_progp[ix]) {
/* the cache will tell us in wich program the function is, and
* where
*/
push_control_stack(cache_pr[ix]);
csp->num_local_variables = num_arg;
current_prog = cache_progp[ix];
pr = cache_pr_inherited[ix];
function_index_offset = cache_function_index_offset[ix];
variable_index_offset = cache_variable_index_offset[ix];
/* Remove excessive arguments */
while(csp->num_local_variables > pr->num_arg) {
pop_stack();
csp->num_local_variables--;
}
/* Correct number of arguments and local variables */
while(csp->num_local_variables < pr->num_arg + pr->num_local) {
push_number(0);
csp->num_local_variables++;
}
tracedepth++;
if (TRACEP(TRACE_CALL)) {
do_trace_call(pr);
}
fp = sp - csp->num_local_variables + 1;
break_sp = (short*)(sp+1);
#ifdef COMPAT_MODE
/* Now, previous_object() is always set, even by
* call_other(this_object()). It should not break any compatibility.
*/
if (current_object != ob)
#endif
previous_ob = current_object;
frame_ob = current_object;
current_object = ob;
save_csp = csp;
eval_instruction(current_prog->program + pr->offset);
#ifdef DEBUG
if (save_csp-1 != csp)
fatal("Bad csp after execution in apply_low\n");
#endif
/*
* Arguments and local variables are now removed. One
* resulting value is always returned on the stack.
*/
return 1;
} /* when we come here, the cache has told us that the function isn't
* defined in the object
*/
} else {
/* we have to search the function */
if (!cache_progp[ix] && cache_id[ix]) {
/* The old cache entry was for an undefined function, so the
name had to be malloced */
free(cache_name[ix]);
}
cache_id[ix] = progp->id_number;
for(pr=progp->functions; pr < progp->functions + progp->num_functions;
pr++)
{
eval_cost++;
if (pr->name == 0 ||
pr->name[0] != fun[0] ||
strcmp(pr->name, fun) != 0 ||
(pr->type & TYPE_MOD_PRIVATE))
{
continue;
}
if (pr->flags & NAME_UNDEFINED)
continue;
/* Static functions may not be called from outside. */
if ((pr->type & TYPE_MOD_STATIC) && current_object != ob)
continue;
/* The searched function is found */
cache_pr[ix] = pr;
cache_name[ix] = pr->name;
push_control_stack(pr);
csp->num_local_variables = num_arg;
current_prog = progp;
pr = setup_new_frame(pr);
cache_pr_inherited[ix] = pr;
cache_progp[ix] = current_prog;
cache_variable_index_offset[ix] = variable_index_offset;
cache_function_index_offset[ix] = function_index_offset;
if (current_object != ob)
previous_ob = current_object;
frame_ob = current_object;
current_object = ob;
save_csp = csp;
eval_instruction(current_prog->program + pr->offset);
#ifdef DEBUG
if (save_csp-1 != csp)
fatal("Bad csp after execution in apply_low\n");
#endif
/*
* Arguments and local variables are now removed. One
* resulting value is always returned on the stack.
*/
return 1;
}
/* We have to mark a function not to be in the object */
cache_name[ix] = string_copy(fun);
cache_progp[ix] = (struct program *)0;
}
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:
/* 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.
*/
static struct svalue *sapply(fun, ob, num_arg)
char *fun;
struct object *ob;
int num_arg;
{
#ifdef DEBUG
struct svalue *expected_sp;
#endif
static struct svalue ret_value = { T_NUMBER };
if (TRACEP(TRACE_APPLY)) {
do_trace("Apply", "", "\n");
}
#ifdef DEBUG
expected_sp = sp - num_arg;
#endif
if (apply_low(fun, ob, num_arg) == 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(fun, ob, num_arg)
char *fun;
struct object *ob;
int num_arg;
{
tracedepth = 0;
return sapply(fun, ob, num_arg);
}
/*
* 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(fun, ob)
char *fun;
struct object *ob;
{
struct function *pr;
#ifdef DEBUG
if (ob->flags & O_DESTRUCTED)
fatal("function_exists() on destructed object\n");
#endif
if (ob->flags & O_SWAPPED)
load_ob_from_swap(ob);
pr = ob->prog->functions;
for(; pr < ob->prog->functions + ob->prog->num_functions; pr++) {
struct program *progp;
if (pr->name[0] != fun[0] || strcmp(pr->name, fun) != 0)
continue;
/* Static functions may not be called from outside. */
if ((pr->type & TYPE_MOD_STATIC) && current_object != ob)
continue;
if (pr->flags & NAME_UNDEFINED)
return 0;
for (progp = ob->prog; pr->flags & NAME_INHERITED;) {
progp = progp->inherit[pr->offset].prog;
pr = &progp->functions[pr->function_index_offset];
}
return progp->name;
}
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(progp, pr)
struct program *progp;
struct function *pr;
{
if (pr->flags & NAME_UNDEFINED)
return;
push_control_stack(pr);
#ifdef DEBUG
if (csp != control_stack)
fatal("call_function with bad csp\n");
#endif
csp->num_local_variables = 0;
current_prog = progp;
pr = setup_new_frame(pr);
previous_ob = current_object;
tracedepth = 0;
eval_instruction(current_prog->program + pr->offset);
pop_stack(); /* Throw away the returned result */
}
/*
* This can be done much more efficiently, but the fix has
* low priority.
*/
static int get_line_number(p, progp)
char *p;
struct program *progp;
{
int offset;
int i;
if (progp == 0)
return 0;
offset = p - progp->program;
#ifdef DEBUG
if (offset > progp->program_size)
fatal("Illegal offset %d in object %s\n", offset, progp->name);
#endif
for (i=0; offset > progp->line_numbers[i]; i++)
;
return i+1;
}
/*
* Write out a trace. If there is an heart_beat(), then return the
* object that had that heart beat.
*/
#if defined(DEBUG) && defined(TRACE_CODE)
char *dump_trace(how)
int how;
#else
char *dump_trace()
#endif
{
struct control_stack *p;
char *ret = 0;
#ifdef DEBUG
int last_instructions PROT((void));
#endif
if (current_prog == 0)
return 0;
if (csp < &control_stack[0]) {
(void)printf("No trace.\n");
debug_message("No trace.\n");
return 0;
}
#ifdef DEBUG
#ifdef TRACE_CODE
if (how)
(void)last_instructions();
#endif
#endif
for (p = &control_stack[0]; p < csp; p++) {
(void)printf("\"%s\" in \"%s\" (\"%s\") line %d\n",
p[0].funp ? p[0].funp->name : "CATCH",
p[1].prog->name, p[1].ob->name,
get_line_number(p[1].pc, p[1].prog));
debug_message("\"%s\" in \"%s\" (\"%s\") line %d\n",
p[0].funp ? p[0].funp->name : "CATCH",
p[1].prog->name, p[1].ob->name,
get_line_number(p[1].pc, p[1].prog));
if (p->funp && strcmp(p->funp->name, "heart_beat") == 0)
ret = p->ob?p->ob->name:0;
}
(void)printf("\"%s\" in \"%s\" (\"%s\") line %d\n",
p[0].funp ? p[0].funp->name : "CATCH",
current_prog->name, current_object->name,
get_line_number(pc, current_prog));
debug_message("\"%s\" in \"%s\" (\"%s\") line %d\n",
p[0].funp ? p[0].funp->name : "CATCH",
current_prog->name, current_object->name,
get_line_number(pc, current_prog));
return ret;
}
int get_line_number_if_any() {
if (current_prog)
return get_line_number(pc, current_prog);
return 0;
}
static char *find_percent(str)
char *str;
{
while(1) {
str = strchr(str, '%');
if (str == 0)
return 0;
if (str[1] != '%')
return str;
str++;
}
}
static int inter_sscanf(num_arg)
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 && arg[0].type != T_OBJECT)
bad_arg(1, F_SSCANF);
if (arg[0].type == T_STRING)
in_string = arg[0].u.string;
else in_string = arg[0].u.ob->name;
if (in_string == 0)
return 0;
/*
* Now get the format description.
*/
if (arg[1].type != T_STRING)
bad_arg(2, F_SSCANF);
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, "F_SSCANF");
arg->u.lvalue->type = T_STRING;
arg->u.lvalue->u.string = make_shared_string(in_string);
arg->u.lvalue->string_type = STRING_SHARED;/**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] != '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, "F_SSCANF");
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;
}
/*
* 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, "F_SSCANF");
arg->u.lvalue->type = T_STRING;
arg->u.lvalue->u.string = make_shared_string(in_string);
arg->u.lvalue->string_type = STRING_SHARED; /**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, "F_SSCANF");
arg->u.lvalue->type = T_STRING;
arg->u.lvalue->u.string = make_shared_string(match);
arg->u.lvalue->string_type = STRING_SHARED;
free(match); /**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(first)
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(a, b)
int a, 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(progp, search_prog)
struct program *progp, *search_prog;
{
int i;
if (!progp || 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(svp, num)
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;
}
}
}
void clear_vector_refs(svp, num)
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.
*/
void check_a_lot_ref_counts(search_prog)
struct program *search_prog;
{
extern struct object *master_ob;
struct object *ob;
struct svalue *p;
/*
* Pass 1: clear the ref counts.
*/
for (ob=obj_list; ob; ob = ob->next_all) {
ob->extra_ref = 0;
ob->prog->extra_ref = 0;
clear_vector_refs(ob->variables, ob->prog->num_variables);
}
for (ob=obj_list_destruct; ob; ob = ob->next_all) {
ob->extra_ref = 0;
ob->prog->extra_ref = 0;
clear_vector_refs(ob->variables, ob->prog->num_variables);
}
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->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);
}
for (ob=obj_list_destruct; ob; ob = ob->next_all)
{
ob->extra_ref++;
count_ref_in_vector(ob->variables, ob->prog->num_variables);
ob->prog->extra_ref++;
if (ob->prog == search_prog)
printf("Found program for destructed 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++; /* marion */
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 */
/*
* Added 3/31/91 by Raistlin.
* added attributes to objects.
*/
static Attribute *find_attribute(attr, obj)
char *attr;
struct object *obj;
{
int i;
for (i=0; i<obj->num_attributes; ++i)
if (strcmp(attr, obj->attributes[i].attribute) == 0)
return &(obj->attributes[i]);
return NULL;
}
static void add_attribute(attr, obj, val, prot)
char *attr;
struct object *obj;
int val;
int prot;
{
int size;
Attribute *a;
if (a = find_attribute(attr, obj))
{
a->value = val;
if (prot)
a->protected = prot;
return;
}
size = ++(obj->num_attributes);
if (obj->attributes)
obj->attributes = (Attribute *)
realloc((char *)obj->attributes, size*sizeof(Attribute));
else
obj->attributes = (Attribute *) xalloc(sizeof(Attribute));
obj->attributes[size-1].attribute = make_shared_string(attr);
obj->attributes[size-1].value = val;
obj->attributes[size-1].protected = prot;
}
static int query_attribute(attr, obj)
char *attr;
struct object *obj;
{
return find_attribute(attr, obj) != 0;
}
static int get_attribute(str, obj)
char *str;
struct object *obj;
{
Attribute *attrib;
if (attrib = find_attribute(str, obj))
return attrib->value;
else
return -1;
}
static struct vector *get_attr_vector(obj)
struct object *obj;
{
struct vector *vec;
int i;
vec = allocate_array(obj->num_attributes*2);
for (i=0; i<obj->num_attributes; ++i)
{
vec->item[i*2].type = T_STRING;
vec->item[i*2].string_type = STRING_SHARED;
vec->item[i*2].u.string=make_shared_string(obj->attributes[i].attribute);
vec->item[i*2+1].type = T_NUMBER;
vec->item[i*2+1].u.number = obj->attributes[i].value;
}
return vec;
}
static void delete_attribute(attr, obj)
char *attr;
struct object *obj;
{
Attribute *del;
if (!(del = find_attribute(attr, obj))) return;
if (del->protected)
{
error("Error, cannot delete protected attribute %s.\n", del->attribute);
return;
}
if (obj->num_attributes == 1)
{
free_string(obj->attributes[0].attribute);
free((char *)obj->attributes);
obj->attributes = NULL;
obj->num_attributes = 0;
return;
}
free_string(del->attribute);
memcpy(del, del+1, (obj->attributes+obj->num_attributes-del-1)*
sizeof(Attribute));
obj->num_attributes--;
if (obj->num_attributes)
obj->attributes = (Attribute *) realloc((char *)obj->attributes,
obj->num_attributes);
else
{
free((char *)obj->attributes);
obj->attributes = NULL;
}
}
static int search_string(str, what, where)
char *str;
int what, where;
{
char *i;
i = (char *) strchr(str+where, what);
if (i) return i-str;
else return -1;
}
int search_array(a, target, ind)
struct svalue *a;
struct svalue *target;
int ind;
{
int ret;
struct svalue orig_targ;
int i;
if (a->type == T_STRING)
{
if (target->type != T_NUMBER)
error("Incompatible argument 2 to searcha on a string.\n");
return search_string(a->u.string, target->u.number, ind);
}
if(a->u.vec->size==0 && ind==0) return -1;
if (ind+1 > a->u.vec->size || ind<0 || ind>MAX_ARRAY_SIZE)
error("Illegal ind to searcha().\n");
orig_targ= *target;
if (target->type==T_OBJECT && (target->u.ob->flags&O_DESTRUCTED)) {
target->type=T_NUMBER;
target->u.number=0;
}
ret = -1;
for (i=ind; i < a->u.vec->size; i++) {
if (a->u.vec->item[i].type!=target->type &&
!(a->u.vec->item[i].type==T_OBJECT &&
(a->u.vec->item[i].u.ob->flags&O_DESTRUCTED) &&
target->type==T_NUMBER))
continue;
switch (target->type) {
case T_OBJECT:
if (a->u.vec->item[i].u.ob==target->u.ob &&
!(a->u.vec->item[i].u.ob->flags&O_DESTRUCTED))
ret = i;
break;
case T_STRING:
if (strcmp(a->u.vec->item[i].u.string,target->u.string)==0)
ret = i;
break;
case T_POINTER:
if (a->u.vec->item[i].u.vec==target->u.vec)
ret = i;
break;
case T_NUMBER:
if (a->u.vec->item[i].u.number==target->u.number)
ret = i;
else
if (a->u.vec->item[i].type==T_OBJECT &&
(a->u.vec->item[i].u.ob->flags&O_DESTRUCTED) &&
target->u.number==0)
ret = i;
break;
default:
break;
}
if (ret != -1)
break;
}
*target=orig_targ;
return ret;
}
struct vector *reallocate_array(a, n)
struct vector *a;
int n;
{
int i,j;
struct vector *p,*ap;
if (n < 0 || n > MAX_ARRAY_SIZE) error("Illegal array size.\n");
p = allocate_array(n);
/*
p = ALLOC_VECTOR(n);
p->ref = 1;
p->size = n;
p->user = current_object->user;
if (p->user) p->user->size_array += n;
*/
j = ((a->size) < n) ? (a->size) : n;
ap = a;
for (i=0; i<n; i++) {
if (i<j) {
p->item[i].type=ap->item[i].type;
switch (p->item[i].type) {
case T_STRING:
p->item[i].u.string =
make_shared_string(ap->item[i].u.string);
p->item[i].string_type = STRING_SHARED;
break;
case T_OBJECT:
if (ap->item[i].u.ob->flags&O_DESTRUCTED) {
p->item[i].type=T_NUMBER;
p->item[i].u.number=0;
} else {
p->item[i].u.ob=ap->item[i].u.ob;
add_ref(p->item[i].u.ob,
"reallocate_array");
}
break;
case T_POINTER:
p->item[i].u.vec=ap->item[i].u.vec;
p->item[i].u.vec->ref++;
break;
case T_NUMBER:
p->item[i].u.number=ap->item[i].u.number;
break;
default:
p->item[i].type=T_NUMBER;
p->item[i].u.number=0;
break;
}
} else {
p->item[i].type=T_NUMBER;
p->item[i].u.number=0;
}
}
return p;
}
struct object *caller()
{
if (frame_ob == 0 || (frame_ob->flags&O_DESTRUCTED))
return 0;
return frame_ob;
}
int rename_file(src, dst)
char *src, *dst;
{
extern int sys_nerr, errno;
extern char *sys_errlist[];
char tmp1[500], tmp2[500];
#ifdef COMPAT_MODE
{
src = check_file_name(src, 0);
if (!src) return 0;
strncpy(tmp1, src, sizeof tmp1);
tmp1[sizeof tmp1 - 1] = 0;
dst = check_file_name(dst, 1);
if (!dst) return 0;
strncpy(tmp2, dst, sizeof tmp2);
tmp2[sizeof tmp2 - 1] = 0;
src = tmp1; dst = tmp2;
}
#else
{
src = check_valid_path(src, current_object->eff_user, "rename", 0);
strcpy(tmp1, src);
dst = check_valid_path(dst, current_object->eff_user, "rename", 1);
strcpy(tmp2, dst);
}
#endif
if (rename(src, dst) == -1)
{
add_message("rename: %s\n",
errno < sys_nerr ? sys_errlist[errno] : "unknown error");
return 0;
}
return 1;
}
int copy_file(src, dst)
char *src, *dst;
{
FILE *src_f, *dst_f;
int c;
char tmp1[500], tmp2[500];
/*
* right about here, it should have an if (!o_flag) check_valid_path
* call, else check_file_name!! Don't know about my syntax though...
* --Buddha
*
* Changed to use COMPAT_MODE -- Raistlin
*/
#ifndef COMPAT_MODE
src = check_valid_path(src, current_object->eff_user, "copy_file", 0);
#else
src = check_file_name(src, 0);
#endif
if (!src) return 0;
strncpy(tmp1, src, sizeof tmp1);
tmp1[sizeof tmp1 - 1] = 0;
/*
* Also there should be one here.
*/
#ifndef COMPAT_MODE
dst = check_valid_path(dst, current_object->eff_user, "copy_file", 1);
#else
dst = check_file_name(dst, 1);
#endif
if (!dst) return 0;
strncpy(tmp2, dst, sizeof tmp2);
tmp1[sizeof tmp2 - 1] = 0;
src = tmp1; dst = tmp2;
src_f = fopen(src, "r");
if (src_f == 0)
return 0;
dst_f = fopen(dst, "w");
if (dst_f == 0) {
fclose(src_f);
return 0;
}
while ((c = fgetc(src_f)) != EOF)
fputc(c, dst_f);
fclose(src_f);
fclose(dst_f);
return 1;
}
struct vector *grab_file(file, start, len)
char *file;
int start, len;
{
FILE *f;
char buf[257];
int line_count=0, char_count=0, i;
struct vector *ret;
#ifndef COMPAT_MODE
file = check_valid_path(file, current_object->eff_user, "grab_file", 0);
#else
file = check_file_name(file, 0);
#endif
if (!file)
return 0;
f = fopen(file, "r");
if (f == 0)
return 0;
if (start < 1) start = 1;
if (len < 1 || len > MAX_ARRAY_SIZE) len = MAX_ARRAY_SIZE;
while (!feof(f))
{
fgets(buf, 256, f);
if (feof(f)) break;
line_count++;
char_count += strlen(buf);
if (char_count > READ_FILE_MAX_SIZE)
{
line_count--;
break;
}
}
if (start > line_count)
{
fclose(f);
return 0;
}
rewind(f);
/* truncate if you try to read past end of file */
if ((line_count-start+1) < len)
len = line_count-start+1;
ret = allocate_array(len);
for (i=0; i<(len+start-1); ++i)
{
char *p;
fgets(buf, 256, f);
if (feof(f)) break;
if (i < start-1) continue;
p = strchr(buf, '\n');
if (p)
*p = 0;
else buf[sizeof buf -1] = 0;
ret->item[i-start+1].type = T_STRING;
ret->item[i-start+1].string_type = STRING_SHARED;
ret->item[i-start+1].u.string = make_shared_string(buf);
}
fclose(f);
return ret;
}
/* Generate a debug message to the player */
static void
do_trace(msg, fname, post)
char *msg, *fname, *post;
{
char buf[10000];
char *objname;
if (!TRACEHB)
return;
if (TRACETST(TRACE_OBJNAME))
{
if (current_object && current_object->name)
objname = current_object->name;
else objname = "??";
}
else objname = "";
sprintf(buf, "*** %d %*s %s %s %s%s", tracedepth, tracedepth, "",
msg, objname, fname, post);
add_message(buf);
}
struct svalue *apply_master_ob(fun, num_arg)
char *fun;
int num_arg;
{
extern struct object *master_ob;
int save_eval_cost = eval_cost;
struct svalue *ret;
eval_cost = 0;
assert_master_ob_loaded();
/*
* Maybe apply() should be called instead ?
*/
ret = sapply(fun, master_ob, num_arg);
eval_cost = save_eval_cost;
return ret;
}
void assert_master_ob_loaded()
{
extern struct object *master_ob;
static int inside = 0;
#ifndef COMPAT_MODE
struct svalue *ret;
#endif
if (master_ob == 0 || master_ob->flags & O_DESTRUCTED) {
/*
* The master object has been destructed. Free our reference,
* and load a new one.
*
* This test is needed because the master object is called from
* yyparse() at an error to find the wizard name. However, and error
* when loading the master object will cause a recursive call to this
* point.
*
* The best solution would be if the yyparse() did not have to call
* the master object to find the name of the wizard.
*/
if (inside) {
fprintf(stderr, "Failed to load master object.\n");
add_message("Failed to load master file !\n");
exit(1);
}
fprintf(stderr, "assert_master_ob_loaded: Reloading master.c\n");
if (master_ob)
free_object(master_ob, "assert_master_ob_loaded");
/*
* Clear the pointer, in case the load failed.
*/
master_ob = 0;
inside = 1;
#ifdef COMPAT_MODE
master_ob = load_object("obj/master", 0);
#else
master_ob = load_object("secure/master", 0);
ret = apply_master_ob("get_root_uid", 0);
if (ret == 0 || ret->type != T_STRING)
fatal ("get_root_uid() in secure/master.c does not work\n");
master_ob->user = add_name(ret->u.string);
master_ob->eff_user = master_ob->user;
#endif
inside = 0;
add_ref(master_ob, "assert_master_ob_loaded");
fprintf(stderr, "Reloading done.\n");
}
}
/*
* When an object is destructed, all references to it must be removed
* from the stack.
*/
void remove_object_from_stack(ob)
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;
}
}
static int
strpref(p, s)
char *p, *s;
{
while (*p)
if (*p++ != *s++)
return 0;
return 1;
}