#include <varargs.h> #include <stdio.h> #include <setjmp.h> #include <string.h> #include <ctype.h> #include <sys/time.h> #include <sys/types.h> /* sys/types.h and netinet/in.h are here to enable include of comm.h below */ #include <sys/stat.h> /* #include <netinet/in.h> Included in comm.h below */ #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 struct object *master_ob; extern void print_svalue PROT((struct svalue *)); 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)); static int strpref PROT((char *, char *)); extern int do_rename PROT((char *, char *)); extern struct object *previous_ob; extern char *last_verb; extern struct svalue const0, const1; struct program *current_prog; 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); 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 onto the stack. */ INLINE void push_object(ob) struct object *ob; { sp++; if (sp == &start_of_stack[EVALUATOR_STACK_SIZE]) fatal("stack overflow\n"); sp->type = T_OBJECT; sp->u.ob = ob; add_ref(ob, "push_object"); } /* * 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) struct svalue *v; { switch(v->type) { case T_STRING: switch(v->string_type) { case STRING_MALLOC: free(v->u.string); break; case STRING_SHARED: free_string(v->u.string); break; } break; case T_OBJECT: free_object(v->u.ob, "free_svalue"); break; case T_POINTER: free_vector(v->u.vec); break; } *v = const0; /* marion - clear this value all away */ } #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_no_free(dest, v); } void push_svalue(v) struct svalue *v; { sp++; assign_svalue_no_free(sp, v); } /* * Pop the top-most value of the stack. * Don't do this if it is a value that will be used afterwards, as the * data may be sent to free(), and destroyed. */ static INLINE void pop_stack() { #ifdef DEBUG if (sp < start_of_stack) fatal("Stack underflow.\n"); #endif free_svalue(sp); sp--; } /* * Compute the address of an array element. */ static INLINE void push_indexed_lvalue() { struct svalue *i, *vec, *item; int ind; i = sp; vec = sp - 1; if (i->type != T_NUMBER || i->u.number < 0) error("Illegal index\n"); ind = i->u.number; 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); 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); /* 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 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 real 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. * This routine is called in three different ways: * push=-1 Pop the stack. * push=1 push the stack. * push=0 No error occured, so the pushed value does not have to be * restored. The pushed value can simply be popped into the void. * * The stack is implemented as a linked list of stack-objects, allocated * from the heap, and deallocated when popped. */ void push_pop_error_context (push) int push; { extern jmp_buf error_recovery_context; extern int error_recovery_context_exists; static struct error_context_stack { jmp_buf old_error_context; int old_exists_flag; struct control_stack *save_csp; struct object *save_command_giver; struct svalue *save_sp; struct error_context_stack *next; } *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 { p = ecsp; if (p == 0) fatal("Catch: error context stack underflow"); if (push == 0) { #ifdef DEBUG if (csp != p->save_csp-1) fatal("Catch: Lost track of csp"); #if 0 /* * This test is not valid! The statement catch(exec("...")) will * change the value of command_giver. */ if (command_giver != p->save_command_giver) fatal("Catch: Lost track of command_giver"); #endif #endif } else { /* push == -1 ! * They did a throw() or error. That means that the control * stack must be restored manually here. */ csp = p->save_csp; pop_n_elems (sp - p->save_sp); command_giver = p->save_command_giver; } memcpy ((char *)error_recovery_context, (char *)p->old_error_context, sizeof error_recovery_context); error_recovery_context_exists = p->old_exists_flag; ecsp = p->next; free ((char *)p); } } /* * 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; struct svalue *expected_stack, *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->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; } if (num_arg != -1) { expected_stack = sp - num_arg + 1; #ifdef DEBUG } 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_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); ob = (sp-1)->u.ob; if (sp->u.number == 0) { ob = ob->shadowed; pop_n_elems(2); if (ob) push_object(ob); else push_number(0); 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; pop_n_elems(2); push_object(ob); break; } 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); *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); *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); 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); 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); *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_CLONE_OBJECT); ob = clone_object(sp->u.string); pop_stack(); if (ob) { sp++; sp->type = T_OBJECT; sp->u.ob = ob; add_ref(ob, "F_CLONE_OBJECT"); } else { push_number(0); } 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); 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); break; CASE(F_WRITE_FILE); i = write_file((sp-1)->u.string, sp->u.string); pop_n_elems(2); push_number(i); 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; } 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_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); 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); pop_n_elems(num_arg); if (num_arg && current_interactive && !(current_interactive->flags & O_DESTRUCTED)) push_object(current_interactive); else if (command_giver && !(command_giver->flags & O_DESTRUCTED)) push_object(command_giver); else push_number(0); break; #ifdef F_FIRST_INVENTORY CASE(F_FIRST_INVENTORY); ob = first_inventory(sp); pop_stack(); if (ob) push_object(ob); else push_number(0); break; #endif /* F_FIRST_INVENTORY */ CASE(F_LIVING); if (sp->u.ob->flags & O_ENABLE_COMMANDS) assign_svalue(sp, &const1); else assign_svalue(sp, &const0); break; #ifdef F_GETUID CASE(F_GETUID); /* * Are there any reasons to support this one in -o mode ? */ 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 0\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); 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 /* F_SETEUID */ #ifdef F_SETUID CASE(F_SETUID) setuid(); push_number(0); break; #endif /* F_SETUID */ #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); 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 third argument 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_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(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(). */ expected_stack = 0; 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_INTERACTIVE); i = (int)sp->u.ob->interactive; 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_MALLOC; sp->u.string = str; } else { push_number(0); } break; } CASE(F_QUERY_SNOOP); { #ifdef COMPAT_MODE struct svalue *arg1; #endif if (command_giver == 0 || sp->u.ob->interactive == 0 || (command_giver->flags & O_DESTRUCTED)) { 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 < 22) { 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); 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_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; } #ifdef F_NEXT_INVENTORY CASE(F_NEXT_INVENTORY); ob = sp->u.ob; pop_stack(); if (ob->next_inv) push_object(ob->next_inv); else push_number(0); break; #endif /* F_NEXT_INVENTORY */ CASE(F_ALL_INVENTORY); { struct vector *vec; vec = all_inventory(sp->u.ob); pop_stack(); if (vec == 0) { push_number(0); } else { push_vector(vec); /* This will make ref count == 2 */ vec->ref--; } break; } CASE(F_DEEP_INVENTORY); { struct vector *vec; vec = deep_inventory(sp->u.ob, 0); free_svalue(sp); 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); else push_number(0); break; CASE(F_THIS_OBJECT); if (current_object->flags & O_DESTRUCTED) push_number(0); else push_object(current_object); break; CASE(F_PREVIOUS_OBJECT); if (previous_ob == 0 || (previous_ob->flags & O_DESTRUCTED)) push_number(0); else push_object(previous_ob); break; #ifdef F_LOCALCMD CASE(F_LOCALCMD); print_local_commands(); push_number(0); break; #endif /* F_LOCALCMD */ 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(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 #ifdef F_ADD_WORTH 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; #endif /* F_ADD_WORTH */ CASE(F_ADD); /*if (inadd==0) checkplus(p);*/ if ((sp-1)->type == T_STRING && sp->type == T_STRING) { char *res; int l = strlen((sp-1)->u.string); res = xalloc(l + strlen(sp->u.string) + 1); (void)strcpy(res, (sp-1)->u.string); (void)strcpy(res+l, sp->u.string); pop_n_elems(2); push_malloced_string(res); } else if ((sp-1)->type == T_NUMBER && sp->type == T_STRING) { char buff[20], *res; sprintf(buff, "%d", (sp-1)->u.number); res = xalloc(strlen(sp->u.string) + strlen(buff) + 1); strcpy(res, buff); strcat(res, sp->u.string); pop_n_elems(2); push_malloced_string(res); } else if (sp->type == T_NUMBER && (sp-1)->type == T_STRING) { char buff[20]; char *res; sprintf(buff, "%d", sp->u.number); res = xalloc(strlen((sp-1)->u.string) + strlen(buff) + 1); strcpy(res, (sp-1)->u.string); strcat(res, buff); pop_n_elems(2); push_malloced_string(res); } else if ((sp-1)->type == T_NUMBER && sp->type == T_NUMBER) { i = sp->u.number + (sp-1)->u.number; sp--; sp->u.number = i; } else if ((sp-1)->type == T_POINTER && sp->type == T_POINTER) { struct vector *v; check_for_destr((sp-1)->u.vec); check_for_destr(sp->u.vec); v = add_array((sp-1)->u.vec,sp->u.vec); pop_n_elems(2); push_vector(v); /* This will make ref count == 2 */ v->ref--; } else { error("Bad type of arg to '+'\n"); } break; CASE(F_SUBTRACT); if ((sp-1)->type == T_POINTER && 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--; /* subtract_array already takes care of destructed objects */ v = subtract_array(sp->u.vec, v); free_vector(sp->u.vec); sp->u.vec = v; break; } if ((sp-1)->type != T_NUMBER) bad_arg(1, F_SUBTRACT); if (sp->type != T_NUMBER) bad_arg(2, F_SUBTRACT); i = (sp-1)->u.number - sp->u.number; sp--; sp->u.number = i; 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; #ifdef F_LOG_FILE CASE(F_LOG_FILE); log_file((sp-1)->u.string, sp->u.string); pop_stack(); break; /* Return first argument */ #endif /* F_LOG_FILE */ 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("call_other() failed\n"); } if (current_object->flags & O_DESTRUCTED) { /* * No external calls may be done when this object is * destructed. */ pop_n_elems(num_arg); push_number(0); 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; len = strlen(arg[0].u.string); if (num_arg == 1) break; /* Simply return argument */ from = arg[1].u.number; if (from < 0) from = len + from; if (from >= len) { pop_n_elems(num_arg); push_string("", STRING_CONSTANT); break; } if (num_arg == 2) { res = string_copy(arg->u.string + from); pop_n_elems(2); push_malloced_string(res); 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 = string_copy(arg->u.string + from); pop_n_elems(3); push_malloced_string(res); 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_malloced_string(res); 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_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_stack(); pop_stack(); push_number(i); break; CASE(F_FILE_NAME); { char *name,*res; /* This function now returns a leading '/', except when -o flag */ name = sp->u.ob->name; #ifdef COMPAT_MODE res = string_copy(name); #else res = add_slash(name); #endif pop_stack(); push_malloced_string(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_FIND_CALL_OUT); i = find_call_out(current_object, sp->u.string); pop_stack(); push_number(i); break; #ifdef F_INHERIT_LIST CASE(F_INHERIT_LIST) { struct vector *vec; extern struct vector *inherit_list PROT((struct object *)); vec = inherit_list(sp->u.ob); pop_stack(); push_vector(vec); break; } #endif /* F_INHERIT_LIST */ CASE(F_WRITE); do_write(sp); break; CASE (F_MEMBER_ARRAY); { struct vector *v; v = sp->u.vec; check_for_destr(v); for (i=0; i < v->size; i++) { if (v->item[i].type != (sp-1)->type) continue; switch((sp-1)->type) { case T_STRING: if (strcmp((sp-1)->u.string, v->item[i].u.string) == 0) break; continue; case T_POINTER: if ((sp-1)->u.vec == v->item[i].u.vec) break; continue; case T_OBJECT: if ((sp-1)->u.ob == v->item[i].u.ob) break; continue; case T_NUMBER: if ((sp-1)->u.number == v->item[i].u.number) break; continue; default: fatal("Bad type to member_array(): %d\n", (sp-1)->type); } break; } if (i == v->size) i = -1; /* Return -1 for failure */ pop_n_elems(2); push_number(i); 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) { #ifdef COMPAT_MODE res = string_copy (str); /* Marion sighs deeply. */ #else res = add_slash(str); #endif if (str = strrchr (res, '.')) *str = 0; push_malloced_string(res); } 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 if (!command_giver || num_arg == 2) { pop_n_elems (num_arg); push_number (0); } else { if (num_arg == 0) { set_snoop(command_giver, 0); push_number(0); } else { /* The argument object is returned. */ 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); 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; } #ifdef F_ADD_VERB CASE(F_ADD_VERB); add_verb(sp->u.string,0); break; #endif /* F_ADD_VERB */ #ifdef F_ADD_XVERB CASE(F_ADD_XVERB); add_verb(sp->u.string,1); break; #endif /* F_ADD_XVERB */ 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 = string_copy(_crypt((sp-1)->u.string, salt)); #else res = string_copy(crypt((sp-1)->u.string, salt)); #endif pop_n_elems(2); push_malloced_string(res); 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_MALLOC); else push_number(0); break; } #endif CASE(F_DESTRUCT); destruct_object(sp); break; CASE(F_RANDOM); if (sp->u.number <= 0) { sp->u.number = 0; break; } sp->u.number = random_number(sp->u.number); break; #ifdef F_SAY CASE(F_SAY); { 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, "say"); say(sp-1, &vtmp.v); } pop_stack(); } break; } #endif /* F_SAY */ #ifdef F_TELL_ROOM 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; } #endif /* F_TELL_ROOM */ #ifdef F_SHOUT CASE(F_SHOUT); shout_string(sp->u.string); break; #endif /* F_SHOUT */ 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); 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_malloced_string(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_malloced_string(str); } 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_malloced_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_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(); see comment above... */ 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(); rw - what the heck ??? */ 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); else push_number(0); } break; #ifdef F_SET_LIGHT 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; } #endif /* F_SET_LIGHT */ 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; cp = string_copy(time_string(sp->u.number)); pop_stack(); push_malloced_string(cp); /* Now strip the newline. */ cp = strchr(cp, '\n'); if (cp) *cp = '\0'; 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_STRING: { char *new_str; if (sp->type == T_STRING) { int l = strlen(argp->u.string); new_str = xalloc(l + strlen(sp->u.string) + 1); strcpy(new_str, argp->u.string); strcpy(new_str+l, sp->u.string); pop_n_elems(2); push_malloced_string(new_str); } else if (sp->type == T_NUMBER) { char buff[20]; sprintf(buff, "%d", sp->u.number); new_str = xalloc(strlen(argp->u.string) + strlen(buff) + 1); strcpy(new_str, argp->u.string); strcat(new_str, buff); pop_n_elems(2); push_malloced_string(new_str); } else { bad_arg(2, F_ADD_EQ); } break; } case T_NUMBER: if (sp->type == T_NUMBER) { i = argp->u.number + sp->u.number; pop_n_elems(2); push_number(i); } else { error("Bad type number to rhs +=.\n"); } break; case T_POINTER: if (sp->type != T_POINTER) { error("Bad type to rhs +=.\n"); } else { struct vector *v; check_for_destr(argp->u.vec); check_for_destr(sp->u.vec); v = add_array(argp->u.vec,sp->u.vec); pop_n_elems(2); push_vector(v); /* This will make ref count == 2 */ v->ref--; } break; default: error("Bad type to lhs +="); } assign_svalue(argp, sp); 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 -="); argp->u.number -= sp->u.number; sp--; break; case T_POINTER: { 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; } default: error("Bad left type to -=.\n"); } assign_svalue_no_free(sp, argp); 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_MALLOC); break; } #endif CASE(F_CINDENT); { 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, "cindent", 1); #endif if (path) { if (indent_program(path)) { assign_svalue(sp, &const1); break; } } else { add_message("Illegal attempt to indent\n"); } assign_svalue(sp, &const0); 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_malloced_string (string_copy (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) { check_for_destr((sp-1)->u.vec); res = make_unique((sp-1)->u.vec, sp->u.string, &const0); } else { check_for_destr((sp-2)->u.vec); res = make_unique((sp-2)->u.vec, (sp-1)->u.string, sp); pop_stack (); } pop_n_elems(2); if (res) { push_vector (res); /* This will make ref count == 2 */ res->ref--; } else push_number (0); break; } CASE(F_VERSION); { char buff[9]; sprintf(buff, "%6.6s%02d", GAME_VERSION, PATCH_LEVEL); push_string(buff, STRING_MALLOC); break; } #ifdef F_RENAME CASE(F_RENAME); { i = do_rename((sp-1)->u.string, sp->u.string); pop_n_elems(2); push_number(i); break; } #endif /* F_RENAME */ 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) { check_for_destr(arg[0].u.vec); 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); /* This will make ref count == 2 */ res->ref--; } else push_number (0); 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; } #ifdef F_ORDER_ALIST 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; } #endif /* F_ORDER_ALIST */ #ifdef F_INSERT_ALIST 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; } #endif /* F_INSERT_ALIST */ #ifdef F_ASSOC 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"); } } 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; } #endif /* F_ASSOC */ #ifdef F_INTERSECT_ALIST 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; } #endif /* F_INTERSECT_ALIST */ #ifdef F_DEBUG_INFO 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; } #endif /* F_DEBUG_INFO */ } #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 */ /* * This object will now be used, and is thus a target for * reset later on (when time due). */ 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) && (!cache_progp[ix] || cache_progp[ix] == ob->prog)) { /* 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 OLD_PREVIOUS_OBJECT_BEHAVIOUR /* 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; 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|TYPE_MOD_PRIVATE)) && 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; #ifdef OLD_PREVIOUS_OBJECT_BEHAVIOUR if (current_object != ob) #endif previous_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. */ char *dump_trace(how) int how; { 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("'%15s' in '%20s' ('%20s')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("'%15s' in '%20s' ('%20s')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; /*crash unliked gc*/ } (void)printf("'%15s' in '%20s' ('%20s')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("'%15s' in '%20s' ('%20s')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) bad_arg(1, F_SSCANF); in_string = arg[0].u.string; if (in_string == 0) return 0; /* * Now get the format description. */ if (arg[1].type != T_STRING) bad_arg(2, F_SSCANF); fmt = arg[1].u.string; /* * First, skip and match leading text. */ for (cp=find_percent(fmt); fmt != cp; fmt++, in_string++) { if (in_string[0] == '\0' || fmt[0] != in_string[0]) return 0; } /* * Loop for every % or substring in the format. Update num_arg and the * arg pointer continuosly. Assigning is done manually, for speed. */ num_arg -= 2; arg += 2; for (number_of_matches=0; num_arg > 0; number_of_matches++, num_arg--, arg++) { int i, type; if (fmt[0] == '\0') { /* * We have reached end of the format string. * If there are any chars left in the in_string, * then we put them in the last variable (if any). */ if (in_string[0]) { free_svalue(arg->u.lvalue); arg->u.lvalue->type = T_STRING; arg->u.lvalue->u.string = string_copy(in_string); arg->u.lvalue->string_type = STRING_MALLOC; number_of_matches++; } break; } #ifdef DEBUG if (fmt[0] != '%') fatal("Should be a %% now !\n"); #endif type = T_STRING; if (fmt[1] == 'd') type = T_NUMBER; else if (fmt[1] != 's') error("Bad type : '%%%c' in sscanf fmt string.", fmt[1]); fmt += 2; /* * Parsing a number is the easy case. Just use strtol() to * find the end of the number. */ if (type == T_NUMBER) { char *tmp = in_string; int tmp_num; tmp_num = (int) strtol(in_string, &in_string, 10); if(tmp == in_string) { /* No match */ break; } free_svalue(arg->u.lvalue); arg->u.lvalue->type = T_NUMBER; arg->u.lvalue->u.number = tmp_num; while(fmt[0] && fmt[0] == in_string[0]) fmt++, in_string++; if (fmt[0] != '%') { number_of_matches++; break; } continue; } /* * Now we have the string case. */ cp = find_percent(fmt); if (cp == fmt) error("Illegal to have 2 adjacent %'s in fmt string in sscanf."); if (cp == 0) cp = fmt + strlen(fmt); /* * First case: There was no extra characters to match. * Then this is the last match. */ if (cp == fmt) { free_svalue(arg->u.lvalue); arg->u.lvalue->type = T_STRING; arg->u.lvalue->u.string = string_copy(in_string); arg->u.lvalue->string_type = STRING_MALLOC; number_of_matches++; break; } for (i=0; in_string[i]; i++) { if (strncmp(in_string+i, fmt, cp - fmt) == 0) { char *match; /* * Found a match ! */ match = xalloc(i+1); (void)strncpy(match, in_string, i); in_string += i + cp - fmt; match[i] = '\0'; free_svalue(arg->u.lvalue); arg->u.lvalue->type = T_STRING; arg->u.lvalue->u.string = match; arg->u.lvalue->string_type = STRING_MALLOC; fmt = cp; /* Advance fmt to next % */ break; } } if (fmt == cp) /* If match, then do continue. */ continue; /* * No match was found. Then we stop here, and return * the result so far ! */ break; } return number_of_matches; } /* test stuff ... -- LA */ #ifdef OPCPROF void opcdump() { int i; for(i = 0; i < MAXOPC; i++) if (opcount[i]) printf("%d: %d\n", i, opcount[i]); } #endif /* * Reset the virtual stack machine. */ void reset_machine(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; /* Clones will not add to the ref count of inherited progs */ if (progp->extra_ref != 1) return; /* marion */ 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; } } } /* * Clear the extra debug ref count for vectors */ 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, and should * only be used for debugging. */ void check_a_lot_ref_counts(search_prog) struct program *search_prog; { extern struct object *master_ob; struct object *ob; /* * 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); } 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); } /* * 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 */ /* 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; objname = TRACETST(TRACE_OBJNAME) ? (current_object && current_object->name ? current_object->name : "??") : ""; sprintf(buf, "*** %d %*s %s %s %s%s", tracedepth, tracedepth, "", msg, objname, fname, post); add_message(buf); } struct svalue *apply_master_ob(fun, num_arg) char *fun; int num_arg; { extern struct object *master_ob; assert_master_ob_loaded(); /* * Maybe apply() should be called instead ? */ return sapply(fun, master_ob, num_arg); } 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; }