/* * NAME: verb.c * DESCRIPTION: inherited by all runtime MOO verb objects */ # define DEBUG 0 inherit "/std/core"; inherit "/std/data"; inherit "/std/bfuns"; inherit "/std/kfuns"; inherit "/std/string"; # if DEBUG inherit "/std/vartext"; # else # define var2str(arg) "" # endif # include <objects.h> # include <moo/data.h> # include <moo/perms.h> # include <moo/verb.h> private int refs; /* references to this object */ private string source; /* tokenized source code for this verb */ int lineno; /* current MOO source line number */ /* * NAME: set_source() * DESCRIPTION: receive source code for this verb */ void set_source(string code) { source = code; } /* * NAME: get_source() * DESCRIPTION: return the source code for this verb */ string *get_source(int full_paren, int indent) { return explode(DETOKEN->main(source, full_paren, indent), "\n"); } /* * NAME: dump_source() * DESCRIPTION: return tokenized verb source (debugging) */ string dump_source(void) { return " " + source; } /* * NAME: ref() * DESCRIPTION: create a reference to this object */ void ref(void) { ++refs; } /* * NAME: del() * DESCRIPTION: delete a reference to this object; if last, self-destruct */ void del(void) { if (! --refs) destruct_object(this_object()); } /* * NAME: get_lineno() * DESCRIPTION: return the current MOO source line number */ int get_lineno(void) { return lineno; } /* * NAME: slist() * DESCRIPTION: construct a spliced list value */ static varargs MOOVAL slist(mixed *info, int *splices, MOOVAL elts...) { int i; for (i = sizeof(splices); i--; ) { int index; MOOVAL elt; elt = elts[index = splices[i]]; if (! LSTP(elt)) return RAISE(E_TYPE); elts = elts[.. index - 1] + elt + elts[index + 1 ..]; } return LST(elts); } /* * NAME: table() * DESCRIPTION: construct a table value (with splices) */ static varargs MOOVAL table(mixed *info, string splices, MOOVAL elts...) { mapping table; int i; for (table = TNEW(), i = sizeof(elts); i; i -= 2) { MOOVAL key, value; key = elts[i - 2]; if (splices && splices[i / 2 - 1] == '@') { if (! TBLP(key)) return RAISE(E_TYPE); TMERGE(table, TBLVAL(key)); continue; } value = elts[i - 1]; TINSERT(table, key, value); } return TBL(table); } /* * NAME: ambaggr() * DESCRIPTION: construct an ambiguous aggregate (list or table, all splices) */ static varargs MOOVAL ambaggr(mixed *info, MOOVAL elts...) { mapping table; MOOVAL *list, elt; int i, type; for (table = TNEW(), list = LNEW(), i = sizeof(elts); i--; ) { elt = elts[i]; if (LSTP(elt)) { if (type == T_TBL) return RAISE(E_TYPE); type = T_LST; elts = elts[.. i - 1] + elt + elts[i + 1 ..]; } else if (TBLP(elt)) { if (type == T_LST) return RAISE(E_TYPE); type = T_TBL; TMERGE(table, TBLVAL(elt)); } else return RAISE(E_TYPE); } if (type == T_LST) return LST(elts); else return TBL(table); } /* * NAME: buffer() * DESCRIPTION: construct a buffer value */ static varargs MOOVAL buffer(mixed *info, string splices, MOOVAL elts...) { MOOVAL elt; string buf, c; int i, sz; buf = ""; c = "x"; for (i = 0, sz = sizeof(elts); i < sz; ++i) { switch (TYPEOF(elt = elts[i])) { case T_NUM: if (splices && splices[i] == '@') return RAISE(E_TYPE); c[0] = NUMVAL(elt); buf += c; break; case T_BUF: if (! splices || splices[i] != '@') return RAISE(E_TYPE); buf += BUFVAL(elt); break; default: return RAISE(E_TYPE); } } return BUF(buf); } /* * NAME: do_assignment() * DESCRIPTION: perform generic indexed assignment */ private MOOVAL do_assignment(mixed *addr, int index, MOOVAL rvalue, int range_flag, MOOVAL *indices) { mixed *handle; MOOVAL lo, hi, lvalue; int i, sz, handle_index; handle = addr; handle_index = index; for (i = 0, sz = sizeof(indices) - (range_flag + 1); i < sz; ++i) { lo = indices[i]; lvalue = addr[index]; if (TBLP(lvalue)) { MOOVAL slot; slot = TLOOKUP(TBLVAL(lvalue), lo); if (STWP(slot)) return STW(E_RANGE); lvalue = addr[index] = TBL(TBLVAL(lvalue) + TNEW()); addr = TINSERT(TBLVAL(lvalue), lo, slot); index = 1; continue; } if (! NUMP(lo) || ! LSTP(lvalue)) return STW(E_TYPE); if (NUMVAL(lo) < 1 || NUMVAL(lo) > sizeof(LSTVAL(lvalue))) return STW(E_RANGE); addr = addr[index] = LST(LSTVAL(lvalue) + LNEW()); index = NUMVAL(lo) - 1; } lvalue = addr[index]; switch (TYPEOF(lvalue)) { case T_STR: if (range_flag) { string old; int b, e; lo = indices[sz]; hi = indices[sz + 1]; if (! NUMP(lo) || ! NUMP(hi) || ! STRP(rvalue)) return STW(E_TYPE); b = NUMVAL(lo); e = NUMVAL(hi); old = STRVAL(lvalue); if (e < 0 || b > strlen(old) + 1) return STW(E_RANGE); if (--b < 0) b = 0; if (e > strlen(old)) e = strlen(old); addr[index] = STR(old[.. b - 1] + STRVAL(rvalue) + old[e ..]); return handle[handle_index]; } else /* ! range */ { string str; str = STRVAL(lvalue); lo = indices[sz]; if (! NUMP(lo) || ! STRP(rvalue)) return STW(E_TYPE); if (strlen(STRVAL(rvalue)) != 1) return STW(E_INVARG); if (NUMVAL(lo) < 1 || NUMVAL(lo) > strlen(str)) return STW(E_RANGE); str[NUMVAL(lo) - 1] = STRVAL(rvalue)[0]; addr[index] = STR(str); return handle[handle_index]; } case T_BUF: if (range_flag) { string old; int b, e; lo = indices[sz]; hi = indices[sz + 1]; if (! NUMP(lo) || ! NUMP(hi) || ! BUFP(rvalue)) return STW(E_TYPE); b = NUMVAL(lo); e = NUMVAL(hi); old = BUFVAL(lvalue); if (e < 0 || b > strlen(old) + 1) return STW(E_RANGE); if (--b < 0) b = 0; if (e > strlen(old)) e = strlen(old); addr[index] = BUF(old[.. b - 1] + BUFVAL(rvalue) + old[e ..]); return handle[handle_index]; } else /* ! range */ { string buf; buf = BUFVAL(lvalue); lo = indices[sz]; if (! NUMP(lo) || ! NUMP(rvalue)) return STW(E_TYPE); if (NUMVAL(lo) < 1 || NUMVAL(lo) > strlen(buf)) return STW(E_RANGE); buf[NUMVAL(lo) - 1] = NUMVAL(rvalue); addr[index] = BUF(buf); return handle[handle_index]; } case T_LST: if (range_flag) { MOOVAL *old; int b, e; lo = indices[sz]; hi = indices[sz + 1]; if (! NUMP(lo) || ! NUMP(hi) || ! LSTP(rvalue)) return STW(E_TYPE); b = NUMVAL(lo); e = NUMVAL(hi); old = LSTVAL(lvalue); if (e < 0 || b > sizeof(old) + 1) return STW(E_RANGE); if (--b < 0) b = 0; if (e > sizeof(old)) e = sizeof(old); --e; addr[index] = LST(old[0 .. b - 1] + LSTVAL(rvalue) + old[e + 1 ..]); return handle[handle_index]; } else /* ! range */ { lo = indices[sz]; if (! NUMP(lo)) return STW(E_TYPE); if (NUMVAL(lo) < 1 || NUMVAL(lo) > sizeof(LSTVAL(lvalue))) return STW(E_RANGE); lvalue = LSTVAL(lvalue) + LNEW(); lvalue[NUMVAL(lo) - 1] = rvalue; addr[index] = LST(lvalue); return handle[handle_index]; } case T_TBL: if (range_flag) return STW(E_TYPE); lo = indices[sz]; lvalue = TBLVAL(lvalue) + TNEW(); TINSERT(lvalue, lo, rvalue); addr[index] = TBL(lvalue); return handle[handle_index]; default: return STW(E_TYPE); } } # ifndef INLINE_PUSHVAR /* * NAME: var() * DESCRIPTION: get variable contents */ static MOOVAL var(mixed *info, int var_index) { return info[I_VARDEFS][var_index - VAR_OFFSET] == '0' ? RAISE(E_VARNF) : VARS[var_index]; } # endif /* * NAME: avar() * DESCRIPTION: perform simple variable assignment */ static MOOVAL avar(mixed *info, int var_index, MOOVAL rvalue) { if (var_index >= VAR_OFFSET) info[I_VARDEFS][var_index - VAR_OFFSET] = '1'; return VARS[var_index] = rvalue; } /* * NAME: avari() * DESCRIPTION: perform indexed variable assignment */ static MOOVAL avari(mixed *info, int var_index, int range_flag, MOOVAL *indices, MOOVAL rvalue) { MOOVAL ret; if (var_index >= VAR_OFFSET && info[I_VARDEFS][var_index - VAR_OFFSET] == '0') RAISE(E_VARNF); ret = do_assignment(VARS, var_index, rvalue, range_flag, indices); if (STWP(ret)) { VARS[var_index] = RAISE(STWVAL(ret)); info[I_VARDEFS][var_index - VAR_OFFSET] = '1'; } return rvalue; } /* * NAME: aprop() * DESCRIPTION: perform simple property assignment */ static MOOVAL aprop(mixed *info, MOOVAL mobj, MOOVAL pname, MOOVAL rvalue) { object ob; MOOVAL ret; if (! OBJP(mobj) || ! STRP(pname)) return RAISE(E_TYPE); if (! (ob = MOOOBJ(OBJVAL(mobj)))) return RAISE(E_INVIND); ret = ob->set_property(STRVAL(pname), rvalue, info); return ret != E_NONE ? RAISE(ret) : rvalue; } /* * NAME: apropi() * DESCRIPTION: perform indexed property assignment */ static MOOVAL apropi(mixed *info, MOOVAL mobj, MOOVAL pname, int range_flag, MOOVAL *indices, MOOVAL rvalue) { MOOVAL current; object ob; int ret; if (! OBJP(mobj) || ! STRP(pname)) return RAISE(E_TYPE); if (! (ob = MOOOBJ(OBJVAL(mobj)))) return RAISE(E_INVIND); current = ob->get_property(STRVAL(pname), info); if (STWP(current)) return RAISE(STWVAL(current)); current = do_assignment( ({ current }), 0, rvalue, range_flag, indices); if (STWP(current)) current = RAISE(STWVAL(current)); ret = ob->set_property(STRVAL(pname), current, info); return ret != E_NONE ? RAISE(ret) : rvalue; } /* * NAME: argcheck() * DESCRIPTION: verify the number of arguments to a builtin function */ static int argcheck(MOOVAL *args, int min, int max) { int size; return (size = sizeof(args)) >= min && (max < 0 || size <= max); } /* * NAME: verbcall() * DESCRIPTION: call a verb */ MOOVAL verbcall(JS_PROTO, MOOVAL mobj, MOOVAL vname, MOOVAL args) { MOOVAL pvar; object ob; JS_BEGIN; if (STRP(mobj)) /* call LPC from MOO */ { string func; if (! STRP(vname)) return RAISE(E_TYPE); if (! WIZARDP(info)) return RAISE(E_PERM); if (catch(ob = load_object(STRVAL(mobj))) || ! ob) return RAISE(E_INVIND); if (! function_object(func = STRVAL(vname), ob)) return RAISE(E_VERBNF); return lpc2moo(call_other(ob, func, moo2lpc(args)...)); } if (! STRP(vname) || ! OBJP(mobj)) return RAISE(E_TYPE); if (! (ob = MOOOBJ(OBJVAL(mobj)))) return RAISE(E_INVIND); pvar = VARS[V_PLAYER]; JS_PREP(1); RET = ob->call_verb(JS_DATA(1), tolower(STRVAL(vname)), ({ (WIZARDP(info) && OBJP(pvar)) ? pvar : OBJ(info[I_PLAYER]), /* player */ mobj, /* this */ OBJ(info[I_THIS]), /* caller */ args, /* args */ VARS[V_ARGSTR], /* argstr */ vname, /* verb */ VARS[V_DOBJ], /* dobj */ VARS[V_DOBJSTR], /* dobjstr */ VARS[V_PREPSTR], /* prepstr */ VARS[V_IOBJ], /* iobj */ VARS[V_IOBJSTR], /* iobjstr */ STD_VARS }) ); JS_END; return STWP(RET) ? RAISE(STWVAL(RET)) : RET; JS_END; } /* * NAME: prep_fork * DESCRIPTION: prepare a fork */ static void prep_fork(mixed *stack, mixed *info, int fork_id) { int task_id, delay; mixed *finfo; finfo = info + ({ }); finfo[I_VARS] += ({ }); finfo[I_TASKID] = task_id = NUMVAL(POP()); finfo[I_DEPTH] = global->get_max_depth(); finfo[I_LINENO] = lineno; finfo[I_LINK] = 0; delay = NUMVAL(POP()); global->register_task(finfo, delay, this_object(), call_out("fork" + (string) fork_id, delay, stack, finfo)); ref(); } /* * NAME: pass() * DESCRIPTION: call the inherited version of a verb */ MOOVAL pass(JS_PROTO, MOOVAL *args) { object host, parent; MOOVAL ret, pvar; JS_BEGIN; if (! (host = info[I_HOSTOBJ]) || ! (parent = host->get_parent())) return RAISE(E_VERBNF); pvar = VARS[V_PLAYER]; JS_PREP(1); RET = parent->call_verb(JS_DATA(1), tolower(info[I_NAME]), ({ (WIZARDP(info) && OBJP(pvar)) ? pvar : OBJ(info[I_PLAYER]), /* player */ OBJ(info[I_THIS]), /* this */ OBJ(info[I_THIS]), /* caller */ LST(args), /* args */ VARS[V_ARGSTR], /* argstr */ STR(info[I_NAME]), /* verb */ VARS[V_DOBJ], /* dobj */ VARS[V_DOBJSTR], /* dobjstr */ VARS[V_PREPSTR], /* prepstr */ VARS[V_IOBJ], /* iobj */ VARS[V_IOBJSTR], /* iobjstr */ STD_VARS }) ); JS_END; return STWP(RET) ? RAISE(STWVAL(RET)) : RET; JS_END; } /* * NAME: abort_task() * DESCRIPTION: called from the global object to abort a call_out() */ void abort_task(int dgd_task_id) { remove_call_out(dgd_task_id); }