/* This software is Copyright 1989, 1990, 1992, 1993 by various individuals. Please see the accompanying file COPYRIGHT for details. */ #include #include #include #include #include #include #include "db.h" #include "inst.h" #include "defs.h" #include "match.h" #include "ctype.h" #include "prims.h" #include "config.h" #include "params.h" #include "strings.h" #include "externs.h" #include "property.h" #include "interface.h" #include "p_for.h" #include "p_file.h" #include "p_time.h" #include "p_tests.h" #include "p_logic.h" #include "p_stack.h" #include "p_array.h" #include "p_create.h" #include "p_strings.h" #include "p_objects.h" #include "p_property.h" #include "p_operands.h" #include "p_descriptor.h" #include "p_conversions.h" #include "p_interaction.h" static void (*prims_function[])(__P_PROTO) = { PRIMS_CONVERSIONS_FL, PRIMS_INTERACTION_FL, PRIMS_LOGIC_FL, PRIMS_OPERANDS_FL, PRIMS_OBJECTS_FL, PRIMS_CREATE_FL, PRIMS_PROPERTY_FL, PRIMS_STACK_FL, PRIMS_STRINGS_FL, PRIMS_TESTS_FL, PRIMS_TIME_FL, PRIMS_DESCRIPTOR_FL, PRIMS_ARRAY_FL, PRIMS_FILE_FL, PRIMS_FOR_FL }; void push(struct inst *, int *, int, ...); int valid_object(struct inst *); static int err; struct frame *allframes; static MUFQUAD frame_serial = 1; static int interp_loop(dbref, dbref, struct frame *, int); extern struct line *read_program(dbref); static int in_critical = 0; static int critddepth; static int critrdepth; static struct inst critvalue; static struct inst ret_inst = { .type = PROG_PRIMITIVE, .data = { .number = IN_RET } }; static int in_library_room(dbref obj) { struct propref *pr; dbref libroom; dbref c; pr = lookup_property(GLOBAL_ENVIRONMENT,"@muf-library-room",0); if (pr == 0) return(0); libroom = propref_get_as_dbref(pr,&dbref_is_room); if (libroom == NOTHING) return(0); DOLIST(c,DBFETCH(libroom)->contents) { if (c == obj) return(1); } return(0); } static char *map_lib_callname(dbref obj, const char *name) { struct propref *pr; static char *nbuf = 0; static int nlen = 0; int l; char *rv; if (name == 0) return(0); l = strlen(name); if (8+l+1 > nlen) { nlen = 8+l+1; nbuf = realloc(nbuf,nlen); } sprintf(nbuf,"_export-%s",name); pr = lookup_property(obj,nbuf,0); if (! pr) return(0); if ((propref_get_attr(pr) & PATTR_TYPE) != PTYPE_STRING) { propref_done(pr); return(0); } rv = propref_get_string(pr); if (rv == 0) rv = dup_string(name); propref_done(pr); return(rv); } static void deref_array(struct mufarray *a) { int i; int n; a->refcnt --; if (a->refcnt > 0) return; n = 1; for (i=a->ndims-1;i>=0;i--) n *= a->dims[i]; for (n--;n>=0;n--) interp_clear(a->data+n); free(a->data); free(a->dims); free(a); } static void ref_array(struct mufarray *a) { a->refcnt ++; } static void deref_ofile(OFILE *o) { o->refcnt --; if (o->refcnt > 0) return; free(o->path); fclose(o->f); free(o); } static void ref_ofile(OFILE *o) { o->refcnt ++; } static void deref_plt(struct plt *plt) { struct plt_prop *pp; plt->refcnt --; if (plt->refcnt > 0) return; while (plt->props) { pp = plt->props; free(pp->name); plt->props = pp->link; free(pp); } free(plt); } static void ref_plt(struct plt *plt) { plt->refcnt ++; } static void deref_cre(struct cre *re) { re->refcnt --; if (re->refcnt > 0) return; free(re->re); free(re); } static void ref_cre(struct cre *re) { re->refcnt ++; } void interp_clear(struct inst *i) { switch (i->type) { case PROG_STRING: free(i->data.string); break; case PROG_PTR: free(i->data.ptr); break; case PROG_RE: deref_cre(i->data.re); break; case PROG_PLT: deref_plt(i->data.plt); break; case PROG_FLOAT: free(i->data.flt); break; case PROG_ARRAY: deref_array(i->data.arr); break; case PROG_QUAD: free(i->data.quad); break; case PROG_OFILE: deref_ofile(i->data.ofile); break; default: if (i->type & PROG__CLEAR) panic("missing PROG__CLEAR type in interp_clear"); break; } i->type = PROG_INTEGER; i->data.number = 0; } static void link_new_frame(struct frame *fr) { if (allframes) allframes->blink = fr; fr->flink = allframes; fr->blink = 0; allframes = fr; } struct frame *new_frame(void) { struct frame *fr; int i; fr = malloc(sizeof(struct frame)); fr->systop = 0; fr->argument.top = 0; for (i=MAX_VAR-1;i>=0;i--) { fr->variables[i].type = PROG_INTEGER; fr->variables[i].data.number = 0; } fr->pc = 0; fr->writeonly = 0; fr->iterations = 0; fr->timeslice = 0; fr->caller = 0; fr->trigger = NOTHING; fr->startcaller = NOTHING; fr->daemon = NOTHING; fr->prog = NOTHING; fr->for_loop = 0; fr->next = 0; link_new_frame(fr); fr->serial = frame_serial ++; return(fr); } #ifdef PARANOID_INTERP static void paranoia(dbref i) { struct frame *f; f = 0; switch (Typeof(i)) { case TYPE_DAEMON: f = DBFETCH(i)->sp.daemon.run; break; case TYPE_PLAYER: f = DBFETCH(i)->sp.player.run; break; } for (;f;f=f->next) if (f->argument.st[0].type & ~0x1f) abort(); } static void full_paranoia(void) { dbref i; for (i=0;iflink) fr->flink->blink = fr->blink; if (fr->blink) fr->blink->flink = fr->flink; else allframes = fr->flink; for (i=0;iargument.top;i++) CLEAR(&fr->argument.st[i]); for (i=0;ivariables[i]); for (drl=fr->caller;drl;drl=tdrl) { tdrl = drl->next; free(drl); } for (four=fr->for_loop;four;four=tfour) { tfour = four->next; free(four); } free(fr); full_paranoia(); } /* this must match the variables[] array in compile.c */ void init_variables(struct frame *f, struct inst me, struct inst loc, struct inst trig, struct inst cmd) { f->variables[0] = me; f->variables[1] = loc; f->variables[2] = trig; f->variables[3] = cmd; } struct inst makeinst(int type, ...) { struct inst i; va_list ap; va_start(ap,type); i.type = type; i.prog = 0; switch (i.type) { case PROG_PRIMITIVE: case PROG_INTEGER: case PROG_VAR: i.data.number = va_arg(ap,int); break; case PROG_STRING: i.data.string = va_arg(ap,char *); break; case PROG_ADD: i.data.call = va_arg(ap,struct inst *); break; case PROG_OBJECT: i.data.objref = va_arg(ap,dbref); break; case PROG_PTR: i.data.ptr = va_arg(ap,struct progptr *); break; case PROG_RE: i.data.re = va_arg(ap,struct cre *); break; case PROG_PLT: i.data.plt = va_arg(ap,struct plt *); break; case PROG_FLOAT: i.data.flt = va_arg(ap,double *); break; case PROG_ARRAY: i.data.arr = va_arg(ap,struct mufarray *); break; default: panic("bad type to makeinst"); break; } va_end(ap); return(i); } struct inst makearray(int nels, ...) { va_list ap; struct mufarray *a; int i; a = malloc(sizeof(struct mufarray)); a->refcnt = 1; a->ndims = 1; a->size = nels; a->dims = malloc(sizeof(int)); a->dims[0] = nels; a->data = malloc(nels*sizeof(struct inst)); va_start(ap,nels); for (i=0;idata[i] = va_arg(ap,struct inst); va_end(ap); return(makeinst(PROG_ARRAY,a)); } int interp_with_me(dbref player, dbref me, dbref program, dbref source, const char *tos_str, struct inst cmd) { struct frame *fr; int return_value; SERIAL player_serial; if ((Typeof(player) != TYPE_PLAYER) && (Typeof(player) != TYPE_DAEMON)) { abort(); return(0); } if (!can_link_to(OWNER(source),TYPE_EXIT,program)) { notify(player,"Program call: Permission denied."); CLEAR(&cmd); return(0); } if (DBFETCH(program)->sp.program.start == 0) { /* try to compile it... */ DBSTORE(program,sp.program.first,read_program(program)); do_compile(player,program,NOTHING); free_prog_text(DBFETCH(program)->sp.program.first); } if (DBFETCH(program)->sp.program.start == 0) { /* compile failed... */ notify(player,"Program not compiled. Cannot run."); CLEAR(&cmd); return(0); } #ifdef TIMESTAMPS DBFETCH(program)->time_used = curtm(); #endif fr = new_frame(); fr->systop = 0; fr->pc = DBFETCH(program)->sp.program.start; fr->writeonly = ( ( (Typeof(source) == TYPE_ROOM) && !Wizard(source) ) || (Typeof(player) == TYPE_DAEMON) ); fr->reading = 0; fr->next = (Typeof(player) == TYPE_PLAYER) ? DBFETCH(player)->sp.player.run : DBFETCH(player)->sp.daemon.run; init_variables( fr, makeinst(PROG_OBJECT,me), makeinst(PROG_OBJECT,DBFETCH(me)->location), makeinst(PROG_OBJECT,source), cmd ); fr->iterations = fr->next ? fr->next->iterations : 0; fr->timeslice = 0; fr->caller = (struct dbref_list *) malloc(sizeof(struct dbref_list)); fr->caller->next = 0; fr->caller->object = me; fr->trigger = source; fr->startcaller = me; fr->prog = program; fr->daemon = 0; push(fr->argument.st,&fr->argument.top,PROG_STRING,dup_string(tos_str)); switch (Typeof(player)) { case TYPE_PLAYER: DBSTORE(player,sp.player.run,fr); break; case TYPE_DAEMON: DBSTORE(player,sp.daemon.run,fr); break; } player_serial = DBFETCH(player)->serial; return_value = interp_loop(player,program,fr,QUANTUM); if (err == 1) err = 0; if ( (player_serial == DBFETCH(player)->serial) && !(FLAGS(player)&INTERACTIVE) ) { prog_abort_frame(player); } return(return_value); } int interp(dbref player, dbref program, dbref source, const char *tos_str, struct inst cmd) { return(interp_with_me(player,player,program,source,tos_str,cmd)); } void interp_resume(dbref player) { struct frame *fr; SERIAL player_serial; fr = (Typeof(player) == TYPE_PLAYER) ? DBFETCH(player)->sp.player.run : DBFETCH(player)->sp.daemon.run; remove_proglock(player,fr->prog); player_serial = DBFETCH(player)->serial; interp_loop(player,fr->prog,fr,QUANTUM); if ( (player_serial == DBFETCH(player)->serial) && !(FLAGS(player)&INTERACTIVE) ) { prog_abort_frame(player); } } int unconnected_interp(int fd, dbref program, dbref source, const char *str, const char *cmd) { struct frame *fr; int return_value; if (!can_link_to(OWNER(source),TYPE_EXIT,program)) { return(0); } if (DBFETCH(program)->sp.program.start == 0) { /* try to compile it... */ DBSTORE(program,sp.program.first,read_program(program)); do_compile(NOTHING,program,NOTHING); free_prog_text(DBFETCH(program)->sp.program.first); } if (DBFETCH(program)->sp.program.start == 0) { /* compile failed... */ return(0); } #ifdef TIMESTAMPS DBFETCH(program)->time_used = curtm(); #endif fr = new_frame(); fr->systop = 0; fr->pc = DBFETCH(program)->sp.program.start; fr->writeonly = 1; fr->reading = 0; fr->next = 0; init_variables( fr, makeinst(PROG_INTEGER,fd), makeinst(PROG_OBJECT,NOTHING), makeinst(PROG_OBJECT,source), makeinst(PROG_STRING,dup_string(cmd)) ); fr->iterations = 0; fr->timeslice = 0; fr->caller = (struct dbref_list *) malloc(sizeof(struct dbref_list)); fr->caller->next = 0; fr->caller->object = NOTHING; fr->trigger = source; fr->startcaller = NOTHING; fr->prog = program; fr->daemon = 0; if (*str && Cisspace(*str)) str ++; push(fr->argument.st,&fr->argument.top,PROG_STRING,dup_string(str)); return_value = interp_loop(NOTHING,program,fr,NOPLAYER_QUANTUM); free_stackframe(fr); return(return_value); } void prog_abort(dbref player) { struct frame **frp; struct frame *fr; frp = (Typeof(player) == TYPE_PLAYER) ? &DBFETCH(player)->sp.player.run : &DBFETCH(player)->sp.daemon.run; while ((fr=*frp)) { *frp = fr->next; free_stackframe(fr); } burn_proglocks(player); if (Typeof(player) == TYPE_DAEMON) { remove_daemon(player); } else { FLAGS(player) &= ~INTERACTIVE; } DBDIRTY(player); } void prog_abort_frame(dbref player) { struct frame **frp; struct frame *fr; if (player == NOTHING) return; frp = (Typeof(player) == TYPE_PLAYER) ? &DBFETCH(player)->sp.player.run : &DBFETCH(player)->sp.daemon.run; fr = *frp; if (fr) { *frp = fr->next; free_stackframe(fr); } if (*frp) { FLAGS(player) |= INTERACTIVE; } else { prog_abort(player); } DBDIRTY(player); } int false(struct inst *p) { return( ( (p->type == PROG_STRING) && (!p->data.string || !*p->data.string) ) || ((p->type == PROG_INTEGER) && (p->data.number == 0)) || ((p->type == PROG_QUAD) && (*p->data.quad == 0)) || ((p->type == PROG_OBJECT) && (p->data.objref == NOTHING)) ); } void copyinst_(struct inst *from, struct inst *to, const char *file, int line) { *to = *from; switch (from->type) { case PROG_STRING: if (from->data.string) to->data.string = dup_string_(from->data.string,file,line); break; case PROG_PTR: to->data.ptr = malloc(sizeof(struct progptr)); *to->data.ptr = *from->data.ptr; break; case PROG_RE: ref_cre(to->data.re); break; case PROG_PLT: ref_plt(to->data.plt); break; case PROG_FLOAT: to->data.flt = malloc(sizeof(double)); *to->data.flt = *from->data.flt; break; case PROG_ARRAY: ref_array(to->data.arr); break; case PROG_QUAD: to->data.quad = malloc(sizeof(*to->data.quad)); *to->data.quad = *from->data.quad; break; case PROG_OFILE: ref_ofile(to->data.ofile); break; default: if (from->type & PROG__CLEAR) panic("missing PROG__CLEAR type in copyinst"); break; } } static struct dbref_list *clone_dbref_list(struct dbref_list *dl) { struct dbref_list *tmp; if (dl == 0) return(0); tmp = malloc(sizeof(struct dbref_list)); *tmp = *dl; tmp->next = clone_dbref_list(dl->next); return(tmp); } static struct for_struct *clone_for_loop(struct for_struct *f) { struct for_struct *tmp; if (f == 0) return(0); tmp = malloc(sizeof(struct for_struct)); *tmp = *f; tmp->next = clone_for_loop(f->next); return(tmp); } static struct frame *clone_frame(struct frame *f) { struct frame *tmp; int i; if (f == 0) return(0); tmp = malloc(sizeof(struct frame)); *tmp = *f; tmp->next = clone_frame(f->next); link_new_frame(tmp); tmp->caller = clone_dbref_list(f->caller); tmp->for_loop = clone_for_loop(f->for_loop); for (i=0;ivariables[i],&tmp->variables[i]); } for (i=tmp->argument.top-1;i>=0;i--) { copyinst(&f->argument.st[i],&tmp->argument.st[i]); } bcopy(&f->sysst[0],&tmp->sysst[0],tmp->systop*sizeof(struct inst *)); return(tmp); } static void debug_line(dbref player, dbref program, const char *msg) { dbref to_tell; to_tell = (player == NOTHING) ? program : player; to_tell = OWNER(to_tell); if (FLAGS(program) & MUCKER) { notify_listener(to_tell,to_tell,msg); } else { notify(to_tell,msg); } } int critical_start(struct frame *fr) { if (in_critical) return(1); in_critical = 1; critrdepth = fr->systop; critddepth = --fr->argument.top; copyinst(fr->argument.st+critddepth,&critvalue); return(0); } static void critical_cancel(void) { if (in_critical) { CLEAR(&critvalue); in_critical = 0; } } static void critical_lose(struct frame *fr) { if (in_critical) { fr->systop = critrdepth; if (fr->systop < 0) panic("critical timeout impossible rstack"); fr->pc = &ret_inst; if (fr->argument.top > critddepth) { stack_pop_n(fr->argument.st,&fr->argument.top,fr->argument.top-critddepth); } copyinst(&critvalue,fr->argument.st+fr->argument.top); fr->argument.top ++; critical_cancel(); } } static void critical_ret(struct frame *fr) { if (in_critical && (fr->systop == critrdepth)) critical_cancel(); } long ilimit = ILIMIT_DEFAULT; #define ABORT_LOOP(s) do { \ interp_err(player,program,insttotext(fr->pc),(s)); \ prog_abort_frame(player); \ return(0); } while (0) /* interp_with_me, interp_resume, unconnected_interp */ static int interp_loop(dbref player, dbref program, struct frame *fr, int quantum) { struct dbref_list *drl; dbref daemon; dbref obj; struct inst *temp1; struct inst *temp2; SERIAL playser; SERIAL progser; full_paranoia(); if (player != NOTHING) { FLAGS(player) &= ~INTERACTIVE; playser = DBFETCH(player)->serial; } if (in_critical) { interp_clear(&critvalue); in_critical = 0; } progser = DBFETCH(program)->serial; if (! fr->pc) panic("interp_loop: no pc"); err = 0; /* This is the 'natural' way to exit a program */ while (fr->systop >= 0) { fr->prog = program; if ((player != NOTHING) && (FLAGS(player) & INTERACTIVE)) { add_proglock(program,player,PROGLOCK_READ); fr->pc ++; fr->reading = 1; critical_cancel(); return(0); } if (fr->timeslice++ >= quantum) /* out of time? */ { critical_lose(fr); if (player != NOTHING) { FLAGS(player) |= INTERACTIVE; add_proglock(program,player,PROGLOCK_READ); fr->timeslice = 0; } return(0); } if ((fr->timeslice % 100) == 0) { unblock_alarm(); block_alarm(); } if (FLAGS(program) & DEBUG) { if ((fr->pc->type != PROG_PRIMITIVE) || (fr->pc->data.number != IN_NOP)) { if (fr->pc == &ret_inst) { debug_line(player,program,"(Return caused by critical section)"); } else { debug_line(player,program,debug_inst(fr->pc,fr->argument.st,fr->argument.top)); } } } /* count them iterations */ if (fr->iterations++ > ilimit) ABORT_LOOP("Iteration overflow."); if (player != NOTHING) paranoia(player); switch (fr->pc->type) { case PROG_INTEGER: case PROG_ADD: case PROG_OBJECT: case PROG_VAR: case PROG_STRING: case PROG_PTR: case PROG_FLOAT: if (fr->argument.top >= STACK_SIZE) ABORT_LOOP("Program Constant: Stack overflow."); copyinst(fr->pc,fr->argument.st+fr->argument.top); fr->argument.top ++; fr->pc ++; break; case PROG_RE: ABORT_LOOP("Compiled regexp as program constant?"); break; #define SETPROGRAM(what) \ do{program=(what);progser=DBFETCH(program)->serial;}while(0) case PROG_PRIMITIVE: /* All pc modifiers and stuff like that should stay here, everything else call with an independent dispatcher. */ switch (fr->pc->data.number) { case IN_NOP: /* don't EVEN ask. You really don't want to know * and even if you did I'd probably be WAY to embarrased * to tell you about it. Just trust me; This is a NOP * or NULL OPERATION and needs to be here. --Doran */ fr->pc ++; break; case IN_IF: if (fr->argument.top < 2) ABORT_LOOP("IF: Stack Underflow."); temp1 = &fr->argument.st[fr->argument.top-1]; temp2 = &fr->argument.st[fr->argument.top-2]; if (temp1->type != PROG_ADD) { ABORT_LOOP("Program internal error: non-address IF. Aborted."); } if (false(temp2)) fr->pc = temp1->data.call; else fr->pc ++; fr->argument.top -= 2; CLEAR(temp1); CLEAR(temp2); break; case IN_LOOP: if (fr->argument.top < 2) ABORT_LOOP("Program internal error: LOOP underflow. Aborted."); temp1 = &fr->argument.st[fr->argument.top-1]; temp2 = &fr->argument.st[fr->argument.top-2]; if (temp1->type != PROG_ADD) { ABORT_LOOP("Program internal error: non-address LOOP. Aborted."); } if (!false(temp2)) fr->pc = temp1->data.call; else fr->pc ++; fr->argument.top -= 2; CLEAR(temp1); CLEAR(temp2); break; case IN_JMP: if (fr->argument.top < 1) ABORT_LOOP("Program internal error: JMP underflow."); temp1 = &fr->argument.st[fr->argument.top-1]; if (temp1->type != PROG_ADD) { ABORT_LOOP("Program internal error: non-address JMP."); } fr->argument.top --; fr->pc = temp1->data.call; CLEAR(temp1); break; case IN_EXECUTE: if (fr->argument.top < 1) ABORT_LOOP("Program word: Stack Underflow."); temp1 = &fr->argument.st[fr->argument.top-1]; if (temp1->type != PROG_ADD) { ABORT_LOOP("EXEC: Non-address argument."); } if (fr->systop >= STACK_SIZE) { ABORT_LOOP("Program word: Stack Overflow"); } fr->argument.top --; fr->sysst[fr->systop++] = fr->pc + 1; fr->pc = temp1->data.call; CLEAR(temp1); break; case IN_EXEC: if (fr->argument.top < 1) ABORT_LOOP("EXEC: Stack Underflow."); temp1 = &fr->argument.st[fr->argument.top-1]; if (temp1->type != PROG_PTR) { ABORT_LOOP("EXEC: Non-pointer argument."); } if ( (player == NOTHING) && (temp1->data.ptr->prog != program) ) { ABORT_LOOP("EXEC: No inter-program calls without a player."); } if (fr->systop >= STACK_SIZE) { ABORT_LOOP("EXEC: Stack Overflow"); } fr->argument.top --; fr->sysst[fr->systop++] = fr->pc + 1; fr->pc = temp1->data.ptr->ptr; if (program != temp1->data.ptr->prog) { add_proglock(program,player,PROGLOCK_READ); SETPROGRAM(temp1->data.ptr->prog); } CLEAR(temp1); break; case IN_CALL: if (player == NOTHING) ABORT_LOOP("CALL: Can't call without a player."); if (fr->argument.top < 1) ABORT_LOOP("CALL: Stack Underflow."); temp1 = &fr->argument.st[fr->argument.top-1]; if ( !valid_object(temp1) || (Typeof(temp1->data.objref) != TYPE_PROGRAM) ) { ABORT_LOOP("CALL: Invalid object."); } obj = temp1->data.objref; /* "UID" is from prims.h */ if ((OWNER(obj) != UID) && !Linkable(obj) && !Wizard(program)) { ABORT_LOOP("CALL: Permission denied."); } if (fr->systop >= STACK_SIZE) ABORT_LOOP("CALL: Stack Overflow"); if (DBFETCH(obj)->sp.program.start == 0) { /* try to compile it... */ DBSTORE(obj,sp.program.first,read_program(obj)); do_compile(OWNER(player),obj,NOTHING); free_prog_text(DBFETCH(obj)->sp.program.first); } if (DBFETCH(obj)->sp.program.start == 0) ABORT_LOOP("CALL: Program not compiled."); fr->argument.top --; CLEAR(temp1); fr->sysst[fr->systop++] = fr->pc + 1; fr->pc = DBFETCH(obj)->sp.program.start; drl = (struct dbref_list *) malloc(sizeof(struct dbref_list)); drl->next = fr->caller; drl->object = program; fr->caller = drl; add_proglock(program,player,PROGLOCK_READ); SETPROGRAM(obj); break; case IN_FORK: if (player == NOTHING) ABORT_LOOP("FORK: Can't fork without a player."); if (fr->argument.top >= STACK_SIZE) ABORT_LOOP("FORK: Stack overflow."); obj = UID; /* OWNER(player) */ if ( DBFETCH(obj)->sp.player.daemons >= ( Wizard(obj) ? MAX_WIZ_DAEMONS : MAX_DAEMONS ) ) ABORT_LOOP("FORK: too many pending processes."); fr->argument.st[fr->argument.top].type = PROG_INTEGER; fr->argument.st[fr->argument.top].data.number = 0; fr->argument.top ++; fr->pc ++; daemon = new_daemon(obj,program,0); switch (Typeof(player)) { case TYPE_PLAYER: DBSTORE(daemon,sp.daemon.run,clone_frame(DBFETCH(player)->sp.player.run)); break; case TYPE_DAEMON: DBSTORE(daemon,sp.daemon.run,clone_frame(DBFETCH(player)->sp.daemon.run)); FLAGS(daemon) |= FLAGS(player) & HEAR; break; } DBSTORE(daemon,sp.daemon.run->daemon,daemon); fr->argument.st[fr->argument.top-1].data.number = (int) daemon; break; case IN_PROGRAM: if (fr->argument.top < 1) ABORT_LOOP("Program internal error: PROGRAM underflow."); temp1 = &fr->argument.st[fr->argument.top-1]; if ( !valid_object(temp1) || ( Typeof(temp1->data.objref) != TYPE_PROGRAM) || !(DBFETCH(temp1->data.objref)->sp.program.codevec) ) { ABORT_LOOP("Program internal error: PROGRAM invalid arg."); } fr->argument.top --; SETPROGRAM(temp1->data.objref); if (player != NOTHING) remove_proglock(player,program); drl = fr->caller; fr->caller = drl->next; free(drl); fr->pc ++; CLEAR(temp1); break; case IN_ENDEXEC: if (fr->argument.top < 1) ABORT_LOOP("Program internal error: PROGRAM underflow."); temp1 = &fr->argument.st[fr->argument.top-1]; if ( !valid_object(temp1) || ( Typeof(temp1->data.objref) != TYPE_PROGRAM) || !(DBFETCH(temp1->data.objref)->sp.program.codevec) ) { ABORT_LOOP("Program internal error: ENDEXEC invalid arg."); } fr->argument.top --; if (temp1->data.objref != program) { SETPROGRAM(temp1->data.objref); remove_proglock(player,program); } CLEAR(temp1); fr->pc ++; break; case IN_RET: critical_ret(fr); fr->systop --; fr->pc = (fr->systop < 0) ? 0 : fr->sysst[fr->systop]; fr->timeslice -= 5; /* guarantee time to execute following IN_PROGRAM (in case we were CALLed) */ break; case IN_READ: if (fr->writeonly) ABORT_LOOP("READ: Program is write-only."); if (player == NOTHING) ABORT_LOOP("READ: No player."); if (Typeof(player) != TYPE_PLAYER) ABORT_LOOP("READ: Cannot read from a daemon."); FLAGS(player) |= INTERACTIVE; fr->timeslice = 0; break; case IN_PROMPT: if (fr->writeonly) ABORT_LOOP("PROMPT: Program is write-only."); if (player == NOTHING) ABORT_LOOP("PROMPT: No player."); if (Typeof(player) != TYPE_PLAYER) ABORT_LOOP("PROMPT: Cannot read from a daemon."); if (fr->argument.top < 1) ABORT_LOOP("PROMPT: stack underflow."); temp1 = &fr->argument.st[fr->argument.top-1]; if (temp1->type != PROG_STRING) { ABORT_LOOP("PROMPT: Non-string argument."); } fr->argument.top --; if (temp1->data.string) notify_nnl(player,temp1->data.string); FLAGS(player) |= INTERACTIVE; fr->timeslice = 0; CLEAR(temp1); break; case IN_SLEEP: if (player == NOTHING) ABORT_LOOP("SLEEP: No player."); /* block is not then-part of if! */ { int cycles; fr->iterations = 0; if (fr->argument.top < 1) ABORT_LOOP("SLEEP: stack underflow."); temp1 = &fr->argument.st[fr->argument.top-1]; switch (temp1->type) { case PROG_INTEGER: cycles = temp1->data.number; if (!Wizard(program) && (cycles < 1)) cycles = 1; break; case PROG_OBJECT: if (! valid_object(temp1)) ABORT_LOOP("SLEEP: not a valid object."); cycles = -1; break; default: ABORT_LOOP("SLEEP: invalid argument type."); break; } if (Typeof(player) == TYPE_PLAYER) { if ( DBFETCH(player)->sp.player.daemons >= ( Wizard(player) ? MAX_WIZ_DAEMONS : MAX_DAEMONS ) ) { ABORT_LOOP("SLEEP: too many daemons."); } daemon = new_daemon(player,program,cycles); DBSTORE(daemon,sp.daemon.run,DBFETCH(player)->sp.player.run); DBSTORE(daemon,sp.daemon.run->daemon,daemon); DBFETCH(player)->sp.player.run = fr->next; if (fr->next) FLAGS(player) |= INTERACTIVE; fr->next = 0; } else { daemon = player; FLAGS(daemon) |= INTERACTIVE; daemon_set_time(daemon,cycles); } fr->argument.top --; switch (temp1->type) { case PROG_OBJECT: daemon_sleep(daemon,temp1->data.objref); break; } DBSTORE(daemon,sp.daemon.run->pc,fr->pc+1); add_proglock(program,daemon,PROGLOCK_READ); CLEAR(temp1); return(0); } break; case IN_WAKEUP: if (fr->argument.top < 1) ABORT_LOOP("WAKEUP: stack underflow."); temp1 = &fr->argument.st[fr->argument.top-1]; switch (temp1->type) { case PROG_OBJECT: if (! valid_object(temp1)) { ABORT_LOOP("WAKEUP: not a valid object."); } break; default: ABORT_LOOP("WAKEUP: not an object"); break; } fr->argument.top --; wakeup_sleepers(temp1->data.objref); CLEAR(temp1); fr->pc ++; break; case IN_YIELD: fr->timeslice = quantum; fr->pc ++; break; case IN_TIMED_PROMPT: { struct frame *tfr; if (fr->writeonly) ABORT_LOOP("TIMED-PROMPT: Program is write-only."); if (player == NOTHING) ABORT_LOOP("TIMED-PROMPT: No player."); if (Typeof(player) != TYPE_PLAYER) ABORT_LOOP("TIMED-PROMPT: Cannot read from a daemon."); if (fr->argument.top < 2) ABORT_LOOP("TIMED-PROMPT: stack underflow."); temp1 = &fr->argument.st[fr->argument.top-2]; temp2 = &fr->argument.st[fr->argument.top-1]; if (temp1->type != PROG_STRING) ABORT_LOOP("TIMED-PROMPT: Non-string argument (1)."); if (temp2->type != PROG_INTEGER) ABORT_LOOP("TIMED-PROMPT: Non-integer argument (2)."); fr->pc ++; daemon = new_daemon(player,program,temp2->data.number); tfr = new_frame(); tfr->systop = 0; tfr->pc = fr->pc; tfr->writeonly = 0; tfr->reading = 0; tfr->next = 0; #if PROG_INTEGER & PROG__CLEAR #error This code needs updating! #else { struct inst zero; zero = makeinst(PROG_INTEGER,0); init_variables(tfr,zero,zero,zero,zero); } #endif tfr->iterations = 0; tfr->timeslice = 0; tfr->caller = 0; tfr->trigger = program; tfr->startcaller = fr->startcaller; tfr->prog = program; tfr->daemon = 0; push(tfr->argument.st,&tfr->argument.top,PROG_OBJECT,player); DBSTORE(daemon,sp.daemon.run,tfr); fr->argument.top -= 2; if (temp1->data.string) notify_nnl(player,temp1->data.string); FLAGS(player) |= INTERACTIVE; fr->timeslice = 0; CLEAR(temp1); CLEAR(temp2); fr->argument.st[fr->argument.top++] = makeinst(PROG_OBJECT,daemon); } break; case IN_T_P_READ: /* the tests should be can't-happens */ if (fr->writeonly) ABORT_LOOP("T-P-READ: Program is write-only."); if (player == NOTHING) ABORT_LOOP("T-P-READ: No player."); if (Typeof(player) != TYPE_PLAYER) ABORT_LOOP("T-P-READ: Cannot read from a daemon."); if (fr->argument.top < 2) ABORT_LOOP("T-P-READ: Stack underflow."); temp1 = &fr->argument.st[fr->argument.top-2]; temp2 = &fr->argument.st[fr->argument.top-1]; if (temp1->type != PROG_OBJECT) ABORT_LOOP("T-P-READ: Non-object argument."); obj = temp1->data.objref; if (Typeof(obj) != TYPE_DAEMON) ABORT_LOOP("T-P-READ: Non-daemon argument."); if (player != OWNER(obj)) ABORT_LOOP("T-P-READ: Daemon owner changed."); remove_daemon(obj); CLEAR(temp1); *temp1 = *temp2; fr->argument.top --; fr->pc ++; break; case IN_T_P_ABORT: /* the tests should be can't-happens */ if (fr->writeonly) ABORT_LOOP("T-P-ABORT: Program is write-only."); if (player == NOTHING) ABORT_LOOP("T-P-ABORT: No player."); if (Typeof(player) != TYPE_DAEMON) ABORT_LOOP("T-P-ABORT: Not in a daemon."); if (fr->argument.top < 1) ABORT_LOOP("T-P-ABORT: Stack underflow."); temp1 = &fr->argument.st[fr->argument.top-1]; if (temp1->type != PROG_OBJECT) ABORT_LOOP("T-P-ABORT: Non-object argument."); obj = temp1->data.objref; if (Typeof(obj) != TYPE_PLAYER) ABORT_LOOP("T-P-ABORT: Non-player argument."); if (! (FLAGS(obj) & INTERACTIVE)) ABORT_LOOP("T-P-ABORT: Non-INTERACTIVE player."); { struct frame *tfr; tfr = DBFETCH(obj)->sp.player.run; if (tfr->argument.top < 1) ABORT_LOOP("T-P-ABORT: Player lost its stack entry."); temp1 = &tfr->argument.st[tfr->argument.top-1]; if (temp1->type != PROG_OBJECT) ABORT_LOOP("T-P-ABORT: Player TOS is not a dbref."); if (temp1->data.objref != player) ABORT_LOOP("T-P-ABORT: Player TOS is wrong dbref."); CLEAR(temp1); tfr->argument.st[tfr->argument.top-1] = makeinst(PROG_INTEGER,0); tfr->reading = 0; tfr->pc ++; } interp_set_fatal_err(); break; case IN_LIBCALL: { char *callname; int i; struct stab *stab; if (player == NOTHING) ABORT_LOOP("LIBCALL: Can't call without a player."); if (fr->argument.top < 2) ABORT_LOOP("LIBCALL: Stack Underflow."); temp1 = &fr->argument.st[fr->argument.top-2]; temp2 = &fr->argument.st[fr->argument.top-1]; if ( !valid_object(temp1) || (Typeof(temp1->data.objref) != TYPE_PROGRAM) ) { ABORT_LOOP("LIBCALL: Invalid object (1)."); } if (temp2->type != PROG_STRING) { ABORT_LOOP("LIBCALL: Non-string argument (2)."); } obj = temp1->data.objref; /* "UID" is from prims.h */ if ( (OWNER(obj) != UID) && !Linkable(obj) && !Wizard(program) && !in_library_room(obj) ) { ABORT_LOOP("LIBCALL: Permission denied."); } if (fr->systop >= STACK_SIZE) ABORT_LOOP("LIBCALL: Stack Overflow"); callname = map_lib_callname(obj,temp2->data.string); if (callname == 0) ABORT_LOOP("LIBCALL: Not an exported name."); if (DBFETCH(obj)->sp.program.start == 0) { /* try to compile it... */ DBSTORE(obj,sp.program.first,read_program(obj)); do_compile(OWNER(player),obj,NOTHING); free_prog_text(DBFETCH(obj)->sp.program.first); if (DBFETCH(obj)->sp.program.start == 0) ABORT_LOOP("LIBCALL: Library didn't compile"); } stab = DBFETCH(obj)->sp.program.stabvec; for (i=DBFETCH(obj)->sp.program.stabsiz-1;i>=0;i--) { if (!strcmp(callname,stab[i].name)) break; } free(callname); if (i < 0) ABORT_LOOP("LIBCALL: Undefined name."); fr->argument.top -= 2; CLEAR(temp1); CLEAR(temp2); fr->sysst[fr->systop++] = fr->pc + 1; fr->pc = DBFETCH(obj)->sp.program.codevec + stab[i].off; drl = (struct dbref_list *) malloc(sizeof(struct dbref_list)); drl->next = fr->caller; drl->object = program; fr->caller = drl; add_proglock(program,player,PROGLOCK_READ); SETPROGRAM(obj); } break; default: { SERIAL playser; SERIAL progser; if (player != NOTHING) playser = DBFETCH(player)->serial; progser = DBFETCH(program)->serial; (*prims_function[fr->pc->data.number-1]) (player,program,fr->pc,fr->argument.st,&fr->argument.top,fr); /* this can happen - imagine daemon #1000 doing something like #1 "@kill #1000" FORCE. */ if ( (DBFETCH(program)->serial != progser) || ( (player != NOTHING) && (DBFETCH(player)->serial != playser) ) ) return(0); if (player != NOTHING) { struct frame **frp; frp = (Typeof(player) == TYPE_DAEMON) ? &DBFETCH(player)->sp.daemon.run : &DBFETCH(player)->sp.player.run; if (! *frp) { /* inner prog_abort blew away all frames, probably */ if (! err) err = 1; } else if (*frp != fr) { /* inner program must've dropped into INTERACTIVE */ if (! (FLAGS(player) & INTERACTIVE)) panic("interp_loop: .run change and !INTERACTIVE"); if (Typeof(player) != TYPE_PLAYER) panic("interp_loop: .run change in a daemon"); if ((*frp)->next != fr) panic("interp_loop: .run changed more than one frame"); /* for now: too bad - blow it away */ /* really should do something useful with it */ prog_abort_frame(player); } } if (! err) { fr->pc ++; if (player != NOTHING) FLAGS(player) &= ~INTERACTIVE; } } break; } #undef SETPROGRAM break; default: ABORT_LOOP("Program internal error: unrecognized instruction."); break; } if (err || (DBFETCH(program)->serial != progser)) { if ((err > 1) && (player != NOTHING)) prog_abort(player); return(0); } if ((player != NOTHING) && (DBFETCH(player)->serial != playser)) { prog_abort(player); return(0); } } if (fr->argument.top) { int retval; retval = !false(fr->argument.st+fr->argument.top-1); return(retval); } return(0); } void interp_set_err(void) { err = 1; } void interp_set_fatal_err(void) { err = 2; } int interp_errored(void) { return(err); } void interp_err(dbref player, dbref program, const char *msg1, const char *msg2) { char buf1[BUFSIZ]; char buf2[BUFSIZ]; struct frame *fr; interp_set_err(); if (player == NOTHING) return; fr = (Typeof(player) == TYPE_PLAYER) ? DBFETCH(player)->sp.player.run : DBFETCH(player)->sp.daemon.run; sprintf(&buf1[0],"Programmer Error. Tell %s the following:",NAME(OWNER(program))); sprintf(&buf2[0],"%s in #%d: %s",msg1,(int)program,msg2); debug_line(player,program,&buf1[0]); if (FLAGS(program) & FINALSTACK) { debug_line(player,program,debug_inst(fr->pc,fr->argument.st,fr->argument.top)); } debug_line(player,program,&buf2[0]); } void push(struct inst *stack, int *top, int type, ...) { va_list ap; va_start(ap,type); stack[*top].type = type; switch (type) { case PROG_NONE: stack[*top] = va_arg(ap,struct inst); break; case PROG_INTEGER: case PROG_VAR: stack[*top].data.number = va_arg(ap,int); break; case PROG_STRING: stack[*top].data.string = va_arg(ap,char *); break; case PROG_OBJECT: stack[*top].data.objref = va_arg(ap,dbref); break; case PROG_RE: stack[*top].data.re = va_arg(ap,struct cre *); break; case PROG_PLT: stack[*top].data.plt = va_arg(ap,struct plt *); break; case PROG_FLOAT: { double *d; d = malloc(sizeof(double)); *d = va_arg(ap,double); stack[*top].data.flt = d; } break; case PROG_ARRAY: stack[*top].data.arr = va_arg(ap,struct mufarray *); break; case PROG_QUAD: { MUFQUAD *q; q = malloc(sizeof(MUFQUAD)); *q = va_arg(ap,MUFQUAD); stack[*top].data.quad = q; } break; case PROG_OFILE: stack[*top].data.ofile = va_arg(ap,OFILE *); break; default: abort(); break; } va_end(ap); (*top)++; } int valid_player(struct inst *oper) { return( (oper->type == PROG_OBJECT) && (oper->data.objref < db_top) && (oper->data.objref >= 0) && (Typeof(oper->data.objref) == TYPE_PLAYER) ); } int valid_object(struct inst *oper) { return( (oper->type == PROG_OBJECT) && (oper->data.objref < db_top) && (oper->data.objref >= 0) && (Typeof(oper->data.objref) != TYPE_GARBAGE) ); } int is_home(struct inst *oper) { return((oper->type == PROG_OBJECT) && (oper->data.objref == HOME)); } int permissions(dbref player, dbref thing) { if ((thing == player) || (thing == HOME)) return(1); switch (Typeof(thing)) { case TYPE_EXIT: return((OWNER(thing) == player) || (OWNER(thing) == NOTHING)); break; case TYPE_ROOM: case TYPE_THING: case TYPE_PROGRAM: case TYPE_DAEMON: return(OWNER(thing) == player); break; } return(0); } void stack_pop_n(struct inst *st, int *topp, int n) { int top; if (n < 1) return; top = *topp; if (n > top) panic("stack_pop_n: too much to pop"); for (;n>0;n--) { top --; CLEAR(&st[top]); } *topp = top; }