/
CDC-1.2b/
CDC-1.2b/src/
parent $builder
object $programmer

var $root child_index 33
var $root owners [$programmer]
var $root fertile 0
var $has_commands commands [["@show *", 'show_cmd], ["@list|@nlist *", 'list_cmd], ["@program *", 'program_cmd], ["@d?isplay *", 'display_cmd], ["@dump *", 'dump_cmd], ["@id *", 'id_cmd], ["@go *", 'go_cmd], ["@mcom?mands *", 'mcommands_cmd], ["@info *", 'info_cmd], ["@which *", 'which_cmd], ["@mv|@move|@cp|@copy *", 'copy_move_cmd], ["@del-m?ethod|@delm?ethod|@dm *", 'del_method_cmd], ["@add-m?ethod|@addm?ethod|@am *", 'add_method_cmd], ["@eval *", 'eval_cmd], ["@ancestor?s|@ascend *", 'ancestors_cmd], ["@add-c?ommand|@ac *", 'add_command_cmd], ["@del-c?ommand|@dc *", 'del_command_cmd], ["@as * eval *", 'eval_as_cmd], ["@def?iner * this|as * eval *", 'eval_as_to_cmd], ["@descend?ants from * to *", 'descendants_cmd], ["@join *", 'join_cmd], ["@ledit *", 'local_edit_cmd]]
var $has_commands shortcuts [[";*", 'eval_cmd, ["eval", 1]]]
var $has_verbs verbs #[]
var $location contents []
var $located location $nowhere
var $user password "*"
var $user connected_at 0
var $user last_command_at 0
var $user connections []
var $root inited 1
var $root owned [$programmer]
var $programmer eval_prefix 0
var $command_aliases command_aliases []
var $user modes #[]
var $mail_list letters #[]
var $mail_list letters_index #[]
var $mail_list senders 1
var $mail_list readers [$core]
var $mail_list notify [$programmer]
var $mail_list last_letter 0
var $mail_ui subscribed #[[$programmer, 791485891]]
var $gendered gender $gender_neuter
var $located obvious 1
var $described prose #[]
var $user prompt ""
var $root manager $programmer
var $root writable [$programmer]
var $root readable ['parameters, 'methods, 'code]
var $user parsers [$command_parser, $verb_parser]
var $user tell_traceback ['brief, 0]
var $user context #[]
var $root quota 75000
var $root dbref 'programmer
var $named name ['uniq, "Generic Programmer"]
var $named name_aliases []
var $user_data user_data #[['real_name, [1, "???"]], ['email, [1, "???"]]]
var $old_has_settings setting_templates #[["@list-options", 'string], ["@program-options", 'string]]
var $programmer eval_tick_offset 0
var $old_command_environment verb_cache #[]
var $old_command_environment command_cache []
var $old_command_environment shortcuts_cache []
var $help_ui current_node $help_node_summary
var $help_ui last_visited #[['pos, 1], ['nodes, [$help_node_summary]]]
var $has_messages message_info #[["teleport.source", [$base_evaluator, $compile_evaluator, $uncompile_evaluator, []]], ["teleport", [$base_evaluator, $compile_evaluator, $uncompile_evaluator, ["actor", "general"]]]]
var $has_messages messages #[[$programmer, #[["teleport", <$message_class, #[["actor", <$ctext_class, ['text_stmt, [['string_type, "You teleport to "], ['get_stmt, [['string_type, "dest name"]]], ['string_type, "."]]]>], ["general", <$ctext_class, ['text_stmt, [['get_stmt, [['string_type, "actor name"]]], ['string_type, " teleports here from "], ['get_stmt, [['string_type, "source name"]]], ['string_type, "."]]]>]]>], ["teleport.source", <$ctext_class, ['text_stmt, [['get_stmt, [['string_type, "actor name"]]], ['string_type, " teleports to "], ['get_stmt, [['string_type, "dest name"]]], ['string_type, "."]]]>]]]]
var $programmer eval_offset 0
var $mail_list mail []
var $mail_ui current #[['location, 0], ['list, $programmer]]

method init_programmer
    .perms(caller(), $root);
    .set_tell_traceback('verbose, 4);
.

method uninit_programmer
    .perms(caller(), $root);
.

method eval_cmd
    arg com, str;
    var result, adjust, vars, v, evalp, ticks, time, mtime, line, offset;
    
    (> .perms(sender(), 'this) <);
    adjust = !str;
    evalp = .eval_prefix();
    vars = ("var " + ((evalp.keys()).to_string(", "))) + "; ";
    for v in (evalp.values())
        vars = (vars + v) + "; ";
    
    // perform escape substitution
    str = .eval_subs(str);
    
    // Evaluate the line.
    if (str && ((str[1]) == ";")) {
        str = substr(str, 2);
        result = .eval([str]);
    } else {
        str = ((vars + " return ") + str) + ";";
        offset = .eval_offset();
        time = [time()];
        ticks = [tick()];
        mtime = [mtime()];
        result = .eval([str]);
        mtime = mtime + [mtime()];
        ticks = ticks + [tick()];
        time = time + [time()];
    
        // this is a rough way to figure mtime, but its the best we can do..
        time = (time[2]) - (time[1]);
        ticks = (ticks[2]) - (ticks[1]);
        if (((mtime[1]) > (mtime[2])) && (!time))
            mtime = (999999 - (mtime[1])) + (mtime[2]);
        else if (((mtime[1]) > (mtime[2])) && time)
            mtime = ((time * 999999) + (999999 - (mtime[1]))) + (mtime[2]);
        else
            mtime = (mtime[2]) - (mtime[1]);
        if (!adjust) {
            time = time - (offset['time]);
            ticks = ticks - (offset['ticks]);
            mtime = mtime - (offset['mtime]);
        }
    
        // figure out times/ticks
        if (adjust && ((result[1]) != 'errors)) {
            eval_offset = eval_offset.add('ticks, ticks);
            eval_offset = eval_offset.add('mtime, mtime);
            eval_offset = eval_offset.add('time, time);
            line = tostr(ticks) + " ticks, ";
            line = (line + tostr(time)) + " seconds, and ";
            line = (line + tostr(mtime)) + " microseconds.";
            .tell("Eval offset adjusted by " + line);
            return;
        }
    }
    
    // Display the errors, or the result.
    if ((result[1]) == 'errors) {
        .tell(result[2]);
    } else {
        .tell("=> " + ($data.unparse(result[2], 'full)));
        if (ticks) {
            line = (("[ ticks: " + tostr(ticks)) + " seconds: ") + tostr(time);
            if (time)
                line = ((line + " (") + tostr(ticks / time)) + " per)";
            .tell(((line + " microseconds: ") + tostr(mtime)) + " ]");
        }
    }
.

method program_cmd
    arg com, args;
    var syn, ref, edited, why, ignore, epref, spref, line;
    
    .perms(sender(), 'this);
    syn = com + " <obj>.<method> [-edited] [comments]";
    args = (args + " ") + ((.setting("@program-options")) || "");
    args = $parse.options(args, #[["edited", [1, ""]]]);
    edited = ((args[2])["edited"])[1];
    args = args[1];
    spref = ((.setting("error-syntax-prefix")) || "=>") + " ";
    epref = (.setting("error-prefix")) || "!  ";
    if (!args) {
        .tell(spref + syn);
        .tell(epref + "No <object>.<method> reference specified.");
        ignore = 1;
    } else {
        catch any {
            ref = $parse.full_reference(args[1], this(), [this(), 'match_environment]);
        } with handler {
            .tell(spref + syn);
            .tell(epref + ((traceback()[1])[2]));
            ref = ['method, 0, 0];
            ignore = 1;
        }
    }
    if ((ref[1]) != 'method) {
        .tell(spref + syn);
        .tell(epref + "Invalid <object>.<method> reference.");
        ignore = 1;
    }
    if ((!edited) && (!($sys.is_admin(this())))) {
        .tell(epref + "Only administrators can compile without history comments.");
        edited = 1;
    }
    if ((ref[2]) && (!((ref[2]).is_writable_by(this())))) {
        .tell(epref + "You cannot program that object.");
        ignore = 1;
    }
    why = sublist(args, 2).to_string();
    args = ['compile, ref[2], ref[3], edited, why];
    if (ignore) {
        .tell(epref + "Ignoring input until \".\" or \"@abort\".");
        args = replace(args, 1, 'ignore);
    }
    if (!ignore)
        line = ((("-- Enter text for " + ((ref[2]).dbref())) + ".") + tostr(ref[3])) + " --";
    else
        line = "";
    .programming_done(.read(line), @args);
.

method show_cmd
    arg com, name;
    var obj;
    
    .perms(sender(), 'this);
    if (!name)
        $parse.tell_error("Must specify an object to show.", ("Syntax: `" + com) + " <object>`");
    obj = .match_env_nice(name);
    catch any {
        .tell(obj.show());
    } with handler {
        .tell((traceback()[1])[2]);
    }
.

method _move_method
    arg syn, ref1, ref2, [opts];
    var line, code;
    
    .perms(sender(), 'this);
    catch any {
        ref1 = $parse.reference(ref1);
        ref2 = $parse.reference(ref2);
        if ((!(ref1[2])) || (!(ref2[2])))
            $parse.tell_error("You must define both a method to be copied from, and one to be copied to.", syn);
        ref1 = replace(ref1, 1, .match_env_nice(ref1[1]));
        ref1 = replace(ref1, 2, tosym(ref1[2]));
        ref2 = replace(ref2, 1, .match_env_nice(ref2[1]));
        ref2 = replace(ref2, 2, tosym(ref2[2]));
        code = (ref1[1]).list_method(ref1[2]);
    
        // Parse Options
        if (opts && ("-edited" in ($list.to_string(opts[1])))) {
            if (!($sys.is_system(sender())))
                .tell("!  Sorry, only admins can turn off the edited messages.");
        } else {
            line = (" // " + ($time.ldate('mdy, 'dash))) + "/";
            line = ((line + ($time.ltime('24hr))) + " ") + (.namef('ref));
            line = (((line + ", moved from ") + ((ref1[1]).dbref())) + ".") + tostr(ref1[2]);
            code = [@code, line];
        }
    
        // delete the old method, compile the new one.
        (ref1[1]).del_method(ref1[2]);
        (ref2[1]).compile(code, ref2[2]);
    
        // cool, we made it.
        line = ((("Method " + ((ref1[1]).dbref())) + ".") + tostr(ref1[2])) + " moved to ";
        line = (((line + ((ref2[1]).dbref())) + ".") + tostr(ref2[2])) + ".";
        .tell(line);
    } with handler {
        switch (error()) {
            case ~methodnf:
                line = (traceback()[1])[2];
                line = substr(line, 1, strlen(line) - 1);
                $parse.tell_error(((line + " on ") + ((ref1[1]).namef('ref))) + ".", syn);
            case ~perm:
                $parse.tell_error("You cannot write on that object.", syn);
            case ~stop:
                rethrow(error());
            default:
                // they are men, they can deal with the tracebacks
                $parse.tell_error([(traceback()[1])[2], traceback()[2], traceback()[3]], syn);
        }
    }
.

method del_method_cmd
    arg com, what;
    var syntax, obj, ref, res;
    
    .perms(sender(), 'this);
    syntax = ("Syntax: `" + com) + " <obj>.<method>`";
    what = explode(what);
    if (!what)
        $parse.tell_error("You must give an object and a method to delete", syntax);
    ref = $parse.reference(what[1]);
    obj = .match_env_nice(ref[1], syntax);
    ref = (ref[2]) || ($parse.tell_error("No method name given.", syntax));
    res = (| obj.del_method(tosym(ref)) |);
    if (type(res) == 'error) {
        switch (res) {
            case ~methodnf:
                .tell("!  No such method");
            default:
                .tell("!  You do not have the perms to modify that object");
        }
    } else {
        .tell("Method removed.");
    }
.

method move_cmd
    arg com, [args];
    var syn;
    
    .perms(sender(), 'this);
    syn = ("`" + com) + " [from] <$obj.ref> [to] <$obj.ref>`";
    
    // figure up args
    args = explode(@args);
    if (args && ((args[1]) == "from"))
        args = delete(args, 1);
    if ((listlen(args) > 1) && ((args[2]) == "to"))
        args = delete(args, 2);
    if (listlen(args) < 2)
        $parse.tell_error("Not enough arguments sent.", syn);
    
    // what type is it (method/parameter)
    if (("." in (args[1])) && ("." in (args[2])))
        return ._move_method(syn, @args);
    else if (("," in (args[1])) && ("," in (args[2])))
        return ._move_parameter(syn, @args);
    else
        $parse.tell_error("You must define both methods, or both parameters.", syn);
.

method list_cmd
    arg com, args;
    var code, nums, line, ref, syntax, ancestor, opt, opts, str;
    
    (> .perms(sender(), 'this) <);
    nums = com == "@nlist";
    syntax = com + " <object>.<method>";
    
    // parse arguments
    opts = #[["n", [0, ""]]];
    args = $parse.options(args, opts);
    opts = args[2];
    args = args[1];
    str = args ? args[1] | "";
    nums = (com == "@nlist") || ((opts["n"])[1]);
    
    // figure out the object/reference
    catch any {
        ref = $parse.full_reference(str, this(), [this(), 'match_environment]);
        if ((ref[1]) != 'method)
            $parse.tell_error("You must submit an object.method() reference", syntax);
        ancestor = (ref[2]).find_method(ref[3]);
        code = ancestor.list_method(ref[3]);
        if (nums) {
            code = $list.numbered_text(code);
        } else {
            for line in [1 .. listlen(code)]
                code = replace(code, line, "  " + (code[line]));
        }
    } with handler {
        switch (error()) {
            case ~methodnf:
                line = ((((ref[2]).dbref()) + ".") + tostr(ref[3])) + "()";
                $parse.tell_error(line + " not found.", syntax);
            default:
                $parse.tell_error((traceback()[1])[2], syntax);
        }
        $parse.tell_error((traceback()[1])[2], syntax);
    }
    opt = (.setting("@list-options")) || "";
    line = ((((ancestor.dbref()) + ".") + tostr(ref[3])) + " ") + opt;
    if (nums)
        .tell("--- Method " + line);
    else
        .tell("@program " + line);
    .tell(code);
    .tell(".");
.

method copy_move_cmd
    arg com, [args];
    var syn, how, line;
    
    .perms(sender(), 'this);
    syn = ("`" + com) + " [from] <$obj.ref> [to] <$obj.ref>`";
    
    // figure up args
    args = explode(@args);
    args = [@args, ""];
    args = setremove(args, "from");
    args = setremove(args, "to");
    if (listlen(args) != 3)
        $parse.tell_error("Send two object references.", syn);
    how = (com in ["@mv", "@move"]) ? 'move | 'copy;
    catch ~namenf {
        args = [$parse.full_reference(args[1], sender()), args[2], args[3]];
        args = [args[1], $parse.full_reference(args[2], sender()), args[3]];
        if ((((args[1])[1]) != ((args[2])[1])) && (((args[2])[1]) != 'unknown))
            $parse.tell_error(((((("Cannot " + tostr(how)) + ((((args[1])[1]) == 'unknown) ? " an object" | ("from a " + tostr((args[1])[1])))) + " to ") + "a ") + tostr((args[2])[1])) + ".", syn);
        else if (((args[1])[1]) == 'method)
            ._copy_move_method(syn, how, @args);
        else if (((args[1])[1]) == 'parameter)
            .tell("Parameters are currently unsupported, sorry!");
        else
            $parse.tell("You must specify a full reference for the source object.", syn);
    } with handler {
        $parse.tell_error((traceback()[1])[2], syn);
    }
.

method display_cmd
    arg com, disp_ref;
    var to_show;
    
    // display method/parameter info for an object
    // to_show is a list as follows:
    // [$object, 'param | 'meth | 'none, "pattern", [opts]]
    .perms(sender(), 'this);
    to_show = ._parse_disp_ref(disp_ref);
    if (to_show) {
        if ((to_show[4])['header])
            ._disp_obj_header(to_show[1], to_show[4]);
        ._disp_obj_detail(@to_show);
        .tell($string.center(" + Finis + ", .linelen(), "-"));
    }
.

method id_cmd
    arg verb, obj;
    
    .perms(sender(), 'this);
    obj = .match_env_nice(obj);
    .tell((((((((obj.namef('xref)) + " ") + ($object.see_perms(obj))) + " ") + ($data.unparse(obj.parents()))) + " ") + tostr(obj.size())) + " bytes");
.

method dump_cmd
    arg com, args;
    var opts, obj, dbref, x, pdbref, par, data, readable, me, code, syn, tail;
    
    .perms(sender(), 'this);
    syn = [["Options can be:", "    +/-n     -- nice formatting", "    +/-t     -- textdump formatting", "    +/-m     -- show/don't show methods", "    +/-p     -- show/don't show parameters", "Flags default to \"-n +m +p\""], com + " [options] <object>"];
    opts = #[["n", [0, 0]], ["t", [1, 0]], ["m", [1, 0]], ["p", [1, 0]], ["e", [0, 0]]];
    args = $parse.options(args, opts);
    if (!(args[1]))
        $parse.tell_error(@syn);
    obj = .match_env_nice((args[1])[1]);
    me = this();
    dbref = obj.dbref();
    opts = args[2];
    readable = obj.is_readable_by(me);
    data = (| obj.data() |);
    if ((opts["p"])[1]) {
        if (!data) {
            .tell(" *** Parameters are unreadable by you ***");
        } else {
            if ((opts["n"])[1]) {
                .tell("Object: " + (obj.namef('ref)));
                .tell("Parents: " + ($list.to_english($list.map(obj.parents(), 'namef))));
                .tell(("Size: " + tostr(obj.size())) + " bytes");
            } else {
                // I'm not sure how the latest drivers handle dbrefs vs objnums
                for x in (obj.parents())
                    .tell("parent " + (x.dbref()));
                .tell("object " + dbref);
                .tell("");
            }
            for par in (data) {
                if (type(par[2]) == 'string) {
                    .tell((" *** " + ((par[1]).dbref())) + "'s parameters are unreadable by you ***");
                } else {
                    pdbref = (| (par[1]).dbref() |) || toliteral(par[1]);
                    for x in (par[2])
                        .tell((((("var " + pdbref) + " ") + tostr(x[1])) + " ") + ($data.unparse(x[2])));
                }
            }
        }
    }
    if ((opts["m"])[1]) {
        .tell("");
        if (!('code in readable)) {
            .tell((" *** " + dbref) + "'s methods are unreadable by you ***");
        } else {
            if ((opts["e"])[1])
                tail = " -edited";
            else
                tail = "";
            for x in (obj.methods()) {
                code = obj.list_method(x);
                if ((opts["n"])[1])
                    .tell(((("@program " + dbref) + ".") + tostr(x)) + tail);
                else
                    .tell("method " + tostr(x));
                for x in [1 .. listlen(code)]
                    code = replace(code, x, "    " + (code[x]));
                .tell(code);
                .tell([".", ""]);
    
                // this can get long
                pause();
            }
        }
    }
.

method add_method_cmd
    arg com, what;
    var syntax, obj, ref, res;
    
    .perms(sender(), 'this);
    syntax = "Syntax: `@add-method <obj>.<method>`";
    what = explode(what);
    if (!what)
        $parse.tell_error("You must give an object and a method to add", syntax);
    ref = $parse.reference(what[1]);
    obj = .match_env_nice(ref[1], syntax);
    ref = (ref[2]) || ($parse.tell_error("No method name given.", syntax));
    res = (| obj.compile([""], tosym(ref)) |);
    if (res != [])
        .tell("!  You do not have the perms to modify that object");
    else
        .tell("Method added.");
.

method descendants_cmd
    arg cmd, from, obj, prep, base;
    
    (> .perms(sender(), 'this) <);
    if (base == "base")
        base = 0;
    else
        base = .match_env_nice(base);
    obj = .match_env_nice(obj);
    .tell((("Descendants of " + (obj.namef('xref))) + (base ? " to " + (base.namef('xref)) | "")) + ":");
    .tell(obj._display_descendants("", [], base));
    .tell("---");
.

method ancestors_cmd
    arg verb, what;
    var thing;
    
    thing = .match_env_nice(what);
    if (!thing)
        return;
    .tell(("Ancestors of " + (thing.namef('ref))) + ":");
    .tell(thing._display_ancestors());
    .tell("---");
.

method _parse_disp_ref
    arg disp_ref;
    var sep_pos, sep, obj, obj_name, to_show, opts, ref, pattern, which_det;
    
    opts = explode(disp_ref);
    if (!opts)
        return [];
    ref = opts[1];
    opts = $list.to_string(sublist(opts, 2));
    sep_pos = (":" in ref) || (("." in ref) || ((";" in ref) || ("," in ref)));
    if (sep_pos) {
        obj_name = substr(ref, 1, sep_pos - 1) || (.namef('ref));
        sep = substr(ref, sep_pos, 1);
        pattern = substr(ref, sep_pos + 1) || "*";
        obj = .match_env_nice(obj_name);
        if (sep in [";", ":"]) {
            opts = ($string.trim(opts)) + " anc:";
            if ((opts[1]) == "$") {
                opts = opts + (explode(opts)[1]);
                opts = substr(opts, strlen(explode(opts)[1]) + 1);
            } else {
                opts = opts + ",";
            }
        }
        if ((pattern[1]) == "_")
            opts = "pri" + opts;
        which_det = #[[":", 'meth], [".", 'meth], [";", 'param], [",", 'param]][sep];
        opts = $display_opts.from_str(opts, obj, $list.last(obj.ancestors()));
        return [obj, which_det, pattern, opts];
    } else {
        obj = .match_env_nice(ref);
        opts = $display_opts.from_str(opts, obj, $list.last(obj.ancestors()));
        return [obj, 'none, "", opts];
    }
.

method _disp_obj_detail
    arg obj, which_det, pattern, opts;
    var details, info, detail, len, show_private, param;
    
    .perms(sender(), 'this);
    len = .linelen();
    if (which_det == 'meth) {
        info = obj.method_info(opts['max_parent], $misc, '_display_filter, [pattern], (opts['show_private]) ? [] | ["_*"]);
        details = [["Methods:", ""], ["Lines $parent.method(args)", "First comment or return value"]];
        for detail in (info)
            details = [@details, [(((((((((detail[4]) ? "+" | " ") + ($string.right(tostr(detail[5]), 4))) + " ") + (((detail[1]) != obj) ? (detail[1]).id() | "")) + ".") + tostr(detail[2])) + "(") + (detail[3])) + ")", detail[6]]];
        for detail in (details)
            .tell(pad((pad(detail[1], len / 2) + " ") + (detail[2]), len));
    } else if (which_det == 'param) {
        info = obj.parameter_info(opts['max_parent]);
        details = ["Parameter(s)"];
        for detail in (info) {
            param = tostr(detail[2]);
            if (match_pattern(pattern, param) != 0) {
                detail = (((("  " + (((detail[1]) != obj) ? (detail[1]).namef('id) | "")) + ",") + param) + ": ") + ($data.unparse(detail[3]));
                if ((opts['chop_props]) && (strlen(detail) > (len - 1)))
                    detail = $string.chop(detail, len);
                details = [@details, detail];
            }
        }
        .tell(details);
    }
.

method mcommands_cmd
    arg cmd, what;
    var obj, coms, c, len, line, emote, opts;
    
    // returns all commands in a nice format.
    // god this is getting ugly.
    .perms(sender(), 'this);
    
    // options
    opts = explode(what);
    what = opts[1];
    opts = sublist(opts, 2);
    if (opts && match_begin("-emote", "-e"))
        emote = 1;
    
    //
    if (what == "")
        what = .ancestors();
    else
        what = [.match_env_nice(what)];
    for obj in (what) {
        if ((obj.has_ancestor($has_commands)) || (obj.has_ancestor($has_verbs))) {
            .tell(("Commands on " + (obj.namef('ref))) + ":");
            coms = [];
            coms = coms + ((| obj.shortcuts() |) || []);
            coms = coms + ((| obj.commands() |) || []);
            len = ((.linelen()) - 5) / 2;
            if (!emote) {
                for c in (coms) {
                    line = ("  " + ($string.left(("\"" + (c[1])) + "\"", len))) + "  ";
                    .tell(line + ($data.unparse(c[2])));
                }
            } else {
                for c in (coms) {
                    // they aren't always a _cmd but fk.
                    .tell((((";" + (.dbref())) + ".del_command(") + ($data.unparse(c[2]))) + ")");
                    line = (((";" + (.dbref())) + ".add_command(\"") + (c[1])) + "\", ";
                    .tell((line + ($data.unparse(c[2]))) + ")");
                }
            }
            if ((| obj.verbs() |)) {
                for c in ((obj.verbs()).to_list()) {
                    line = ("  " + ($string.left(("\"" + (c[1])) + "\"", len))) + " ";
                    line = line + ((((c[2])[2]) == 'remote) ? "rmt " | "    ");
                    line = line + ($data.unparse((c[2])[1]));
                    .tell(line);
                }
            }
            if (obj == $has_commands)
                break;
        } else {
            .tell(("Object " + (obj.namef('ref))) + " has no commands.");
        }
    }
.

method _disp_obj_header
    arg obj, opts;
    var line, lines, objs, line_len, extra_str;
    
    // Display header info for obj
    .perms(sender(), 'this);
    line_len = .linelen();
    
    // Object:
    line = "Object: " + (obj.namef('ref));
    if (obj.fertile())
        line = line + " (Fertile)";
    .tell(line);
    
    // Owners:
    if (opts['header]) {
        objs = obj.owners();
        line = $list.to_english($list.map(objs, 'namef, 'ref));
        .tell(("Owner" + ((listlen(objs) > 1) ? "s: " | ": ")) + line);
        .tell(("Size: " + ($integer.to_english(obj.size()))) + " bytes");
    
        // Parents: [parents]
        objs = obj.parents();
        if (objs) {
            line = $list.to_english($list.map(objs, 'namef, 'ref));
            if (!(opts['chop_head_data]))
                line = $string.chop(line, line_len - 9);
            .tell(("Parent" + ((listlen(objs) > 1) ? "s: " | ": ")) + line);
        }
        objs = obj.children();
        if (objs) {
            line = $list.to_english($list.map(objs, 'namef, 'ref));
            if (!(opts['chop_head_data]))
                line = $string.chop(line, line_len - 10);
            .tell(((listlen(objs) > 1) ? "Children: " | "Child: ") + line);
        }
        if (obj.has_ancestor($located))
            .tell("Location: " + ((obj.location()).namef('ref)));
    }
.

method go_cmd
    arg com, where;
    var loc, p;
    
    (> .perms(sender(), 'this) <);
    if (!where) {
        .tell("Specify a destination.");
        return;
    }
    if (where == "home")
        loc = .home();
    else
        loc = (| .match_environment(where) |);
    if (!loc) {
        for p in (.remembered_places()) {
            if ((p.match_name(where)) || (p.match_name_aliases(where))) {
                loc = p;
                break;
            }
        }
    }
    
    // if we have still not found a location...
    if (!loc) {
        catch any {
            loc = $place_db.find(where);
        } with handler {
            switch (error()) {
                case ~ambig:
                    .tell("Several rooms match that name: " + ((((traceback()[1])[3]).map('namef)).to_english()));
                case ~namenf:
                    .tell(("Unable to find place \"" + where) + "\".");
                    return;
                default:
                    $parse.tell_error((traceback()[1])[2]);
            }
        }
    }
    if (!loc) {
        .tell(("Unable to find place \"" + where) + "\".");
        return;
    }
    if (loc == (.location())) {
        .tell("You are already there!");
        return;
    }
    if (!(.teleport(loc)))
        .tell("Sorry.");
.

method info_cmd
    arg com, obj;
    var info;
    
    .perms(sender(), 'this);
    obj = .match_env_nice(obj);
    info = obj.info();
    if (!info)
        return .tell("No information about " + (obj.namef('ref)));
    .tell("-----");
    .tell(info);
    .tell("-----");
.

method eval_subs
    arg code;
    var idx, ret_code, sub;
    
    ret_code = "";
    while (code) {
        idx = "^" in code;
        if (!idx) {
            return ret_code + code;
        } else if ((idx == strlen(code)) || (substr(code, idx + 1, 1) == "^")) {
            ret_code = ret_code + substr(code, 1, idx);
            code = substr(code, idx + 1);
            if (code && ((code[1]) == "^"))
                code = substr(code, 2);
        } else {
            if (idx > 1) {
                ret_code = ret_code + substr(code, 1, idx - 1);
                code = substr(code, idx + 1);
            } else {
                code = substr(code, 2);
            }
            idx = 1;
            while ((idx <= strlen(code)) && (!((code[idx]) in " =.()[]=<>?|&!*+-/';\"")))
                idx = idx + 1;
            sub = .match_env_nice(substr(code, 1, idx - 1));
            ret_code = ret_code + (sub.dbref());
            code = substr(code, idx);
        }
    }
    return ret_code;
.

method eval_prefix
    .perms(sender(), 'this);
    return $dictionary.union(#[["me", "me = this();"], ["here", "here = me.location()"]], eval_prefix || #[]);
.

method del_command_cmd
    arg cmd, str;
    var syn, cmdref, ref;
    
    .perms(sender(), 'this);
    syn = ("`" + cmd) + " <command reference> [from] <object>`";
    if (!str)
        $parse.tell_error("No arguments specified.", syn);
    if ((str[1]) == "\"") {
        str = substr(str, 2);
        cmdref = substr(str, 1, ("\"" in str) - 1);
        str = explode(substr(str, ("\"" in str) + 1));
    } else if ((str[1]) == "'") {
        str = explode(str);
        cmdref = tosym(substr(str[1], 2));
    }
    if (!cmdref)
        $parse.tell_error("Command references can either be templates or symbols.", syn);
    if (!str)
        $parse.tell_error("Invalid object reference.", syn);
    ref = .match_env_nice(str[listlen(str)]);
    catch any {
        ref.del_command(cmdref);
    } with handler {
        switch (error()) {
            case ~perm:
                $parse.tell_error(strsub((traceback()[1])[2], "%O", ((traceback()[1])[3]).namef('xref)), syn);
            default:
                $parse.tell_error((traceback()[1])[2], syn);
        }
    }
    .tell(((("Command " + ((type(cmdref) == 'symbol) ? "for method " | "with template ")) + toliteral(cmdref)) + " deleted from ") + (ref.namef('xref)));
.

method programming_done
    arg code, status, obj, meth, hist, why;
    var last_edit, errors;
    
    (> .perms(sender(), 'this) <);
    if (code == 'aborted)
        return;
    if (status == 'ignore)
        return .tell("Finished ignoring input.");
    if (hist) {
        last_edit = (" // " + ($time.ldate('mdy, 'dash))) + "/";
        last_edit = (last_edit + ($time.ltime('24hr))) + " ";
        last_edit = last_edit + (.namef('ref));
        if (why)
            last_edit = (last_edit + ":") + why;
        code = code + [last_edit];
    }
    catch any {
        errors = obj.compile(code, meth);
        if (errors)
            .tell(errors);
        else
            .tell("Method compiled.");
    } with handler {
        switch (error()) {
            case ~perm:
                $parse.tell_error("!  You cannot program that object.");
            default:
                $parse.tell_error("!  " + ((traceback()[1])[2]));
        }
    }
.

method which_cmd
    arg which, cmdstr;
    var cmd, cmds, parent, pcmds, matches, smatches, template, syn, len, line;
    
    // searches all commands for str and returns obj.method for said command
    .perms(sender(), 'this);
    syn = ("`" + which) + " <template>`";
    if (!cmdstr)
        $parse.tell_error("No template given.", syn);
    matches = [];
    smatches = [];
    template = explode(cmdstr)[1];
    for parent in (.ancestors()) {
        pcmds = (| parent.commands() |);
        if (pcmds) {
            for cmd in (pcmds) {
                if (cmdstr in ($string.strip(cmd[1], "?")))
                    matches = [@matches, [cmd[1], parent, cmd[2]]];
    
                //            else if (match_begin(cmd[1], template))
                //                 matches = [@matches, [cmd[1], parent, cmd[2]]];
            }
        }
    
        // do shortcuts seperately so we can note the difference
        pcmds = (| parent.shortcuts() |);
        if (pcmds) {
            for cmd in (pcmds) {
                if (cmdstr in ($string.strip(cmd[1], "?")))
                    smatches = [@smatches, [cmd[1], parent, cmd[2], cmd[3]]];
    
                //            else if (match_begin(cmd[1], template))
                //                smatches = [@smatches, [cmd[1], parent, cmd[2], cmd[3]]];
            }
        }
        if (parent == $has_commands)
            break;
    }
    if ((!matches) && (!smatches)) {
        .tell(("No commands found matching the template \"" + cmdstr) + "\".");
        return;
    }
    len = (.linelen()) / 2;
    .tell(("Commands matching the template \"" + cmdstr) + "\":");
    for cmd in (matches) {
        line = ("  " + pad($code.unparse_command(delete(cmd, 2)), len)) + "  ";
        .tell((((line + ((cmd[2]).dbref())) + ".") + tostr(cmd[3])) + "()");
    }
    for cmd in (smatches) {
        line = ("  " + pad($code.unparse_command(delete(cmd, 2)), len)) + "  ";
        .tell((((line + ((cmd[2]).dbref())) + ".") + tostr(cmd[3])) + "()");
    }
.

method _copy_move_method
    arg syn, how, ref1, ref2, opts;
    var line, code, type;
    
    .perms(sender(), 'this);
    catch any {
        code = (ref1[2]).list_method(ref1[3]);
        if (opts && ("-edited" in opts)) {
            if (!($sys.is_system(sender())))
                .tell("!  Only admins can turn off the edited messages.");
        } else {
            line = (" // " + ($time.ldate('mdy, 'dash))) + "/";
            line = ((line + ($time.ltime('24hr))) + " ") + (.namef('ref));
            line = (((line + ", moved from ") + ((ref1[2]).dbref())) + ".") + tostr(ref1[3]);
            code = [@code, line];
        }
        if (how == 'move)
            (ref1[2]).del_method(ref1[3]);
        (ref2[2]).compile(code, ref2[3]);
        line = ($string.capitalize(tostr(ref1[1]))) + " ";
        line = (((line + ((ref1[2]).dbref())) + ".") + tostr(ref1[3])) + " ";
        line = (line + ((how == 'move) ? "moved" | "copied")) + " ";
        line = (((line + ((ref2[2]).dbref())) + ".") + tostr(ref2[3])) + ".";
        .tell(line);
    } with handler {
        switch (error()) {
            case ~methodnf:
                line = (traceback()[1])[2];
                line = substr(line, 1, strlen(line) - 1);
                $parse.tell_error(((line + " on ") + ((ref1[1]).namef('ref))) + ".", syn);
            case ~perm:
                $parse.tell_error("You cannot write on that object.", syn);
            case ~stop:
                rethrow(error());
            default:
                // they are men, they can deal with the tracebacks
                .tell("Error encountered:");
                .tell_traceback(traceback());
        }
    }
.

method add_command_cmd
    arg cmd, str;
    var syn, sref, ref, template;
    
    .perms(sender(), 'this);
    syn = ("`" + cmd) + " <template> [for] <object.method>`";
    if (!str)
        $parse.tell_error("No arguments specified.", syn);
    if ((str[1]) != "\"")
        $parse.tell_error("No template given (must be enclosed in quotes).", syn);
    str = substr(str, 2);
    sref = explode(substr(str, ("\"" in str) + 1));
    template = substr(str, 1, ("\"" in str) - 1);
    sref = sref[listlen(sref)];
    ref = (| $parse.full_reference(sref, this()) |);
    if (!ref)
        $parse.tell_error(("\"" + sref) + "\" is an invalid object.method() reference.", syn);
    if (!(ref[3]))
        $parse.tell_error(("No method specified on " + ((ref[2]).dbref())) + ".", syn);
    if (!template)
        $parse.tell_error("No template specified.", syn);
    catch any {
        (ref[2]).add_command(template, ref[3]);
    } with handler {
        .tell_traceback(traceback());
        switch (error()) {
            case ~perm:
                $parse.tell_error(strsub((traceback()[1])[2], "%O", ((traceback()[1])[3]).namef('xref)), syn);
            default:
                $parse.tell_error((traceback()[1])[2], syn);
        }
    }
    .tell(((((("Command " + template) + " added for the method ") + toliteral(ref[3])) + " on ") + ((ref[2]).namef('xref))) + ".");
.

method eval_as_cmd
    arg cmd, objname, prep, line;
    var obj, result;
    
    (> .perms(sender(), 'this) <);
    line = line + ";";
    obj = .match_env_nice(objname);
    result = obj.eval([line]);
    if ((result[1]) == 'errors)
        .tell(result[2]);
    else
        .tell((("eval as " + (obj.dbref())) + " => ") + ($data.unparse(result[2])));
.

method eval_as_to_cmd
    arg cmd, objname, this, targetname, eval, line;
    var obj, target, result;
    
    (> .perms(sender(), 'this) <);
    line = line + ";";
    obj = .match_env_nice(objname);
    target = .match_env_nice(targetname);
    result = obj.eval([line], target);
    if ((result[1]) == 'errors)
        .tell(result[2]);
    else
        .tell((((((((cmd + " ") + objname) + " ") + this) + " ") + targetname) + " eval => ") + ($data.unparse(result[2])));
.

method teleport
    arg dest;
    var m1, m2, source, vars;
    
    (> .perms(sender(), 'this) <);
    source = .location();
    if (!(| .move_to(dest) |))
        return 0;
    vars = $base_evaluator.fix_values(#[["actor name", .name()], ["actor", this()], ["source name", source.name()], ["source", source], ["dest name", dest.name()], ["dest", dest]]);
    m1 = .eval_message("teleport.source", vars);
    m2 = .eval_message("teleport", vars);
    dest.announce(m2);
    source.announce(m1, this());
.

method join_cmd
    arg com, who;
    var loc, p, user;
    
    (> .perms(sender(), 'this) <);
    if (!who) {
        .tell("Specify a user to join.");
        return;
    }
    catch any {
        user = $user_db.find(who);
    } with handler {
        .tell((traceback[1])[2]);
        return;
    }
    loc = user.location();
    if (loc == (.location())) {
        .tell(("You are already with " + (user.name())) + "!");
        return;
    }
    if (!(.teleport(loc))) {
        .tell("Sorry.");
    } else {
        .tell(("You join " + (user.name())) + ".");
        user.tell((.name()) + " joins you.");
    }
.

method local_edit_cmd
    arg cmd, args;
    var syn, ref, edited, why, epref, spref, line, code, ancestor;
    
    syn = cmd + " <obj>.<method> [-edited] [comments]";
    args = $parse.options(args, #[["edited", [1, ""]]]);
    edited = ((args[2])["edited"])[1];
    args = args[1];
    if (!args)
        $parse.tell_error("No <object>.<method> reference specified.", syn);
    catch any {
        ref = $parse.full_reference(args[1], this(), [this(), 'match_environment]);
    } with handler {
        $parse.tell_error((traceback()[1])[2], syn);
    }
    if ((ref[1]) != 'method)
        $parse.tell_error("Invalid <object>.<method> reference.", syn);
    if ((!edited) && (!($sys.is_admin(this())))) {
        .tell("Only administrators can compile without history comments.");
        edited = 1;
    }
    if ((ref[2]) && (!((ref[2]).is_writable_by(this()))))
        $parse.tell_error("You cannot program on that object.");
    why = sublist(args, 2).to_string();
    catch any {
        ancestor = (ref[2]).find_method(ref[3]);
        code = ancestor.list_method(ref[3]);
    } with handler {
        switch (error()) {
            case ~methodnf:
                line = ((((ref[2]).dbref()) + ".") + tostr(ref[3])) + "()";
                $parse.tell_error(line + " not found.", syntax);
            default:
                $parse.tell_error((traceback()[1])[2], syntax);
        }
        $parse.tell_error((traceback()[1])[2], syntax);
    }
    .tell((((((("#$# edit name: " + ((ref[2]).name())) + ".") + tostr(ref[3])) + " upload: @program ") + (ancestor.dbref())) + ".") + tostr(ref[3]));
    .tell(code);
    .tell(".");
.

method xdisplay_cmd
    arg cmd, args;
    var opts, syn, ref;
    
    (> .perms(sender(), 'this) <);
    
    //
    syn = cmd + " <object reference> [options]";
    opts = #[["c", [1, ""]]];
    args = $parse.options(args, opts);
    opts = args[2];
    args = args[1];
    if (!args)
        $parse.tell_error("No object reference specified.", syn);
    catch any {
        ref = $parse.xreference(args[1], this(), [this(), 'match_environment]);
    } with handler {
        $parse.tell_error((traceback()[1])[2], syn);
    }
    .tell(._display_object_detail(ref[2], 0));
    .tell(pad("", .linelen(), "-"));
    .(tosym("_display_" + tostr(ref[1])))(sublist(args, 2), opts);
.

method _display_object_detail
    arg obj, chop;
    var len, out, line, tmp;
    
    (> .perms(sender(), 'this) <);
    len = .linelen();
    line = ("Object: " + (obj.namef('xref))) + " ";
    line = line + ($object.see_perms(obj));
    out = [((line.left((len / 3) * 2)) + " Size: ") + ((obj.size()).to_english())];
    line = obj.owners();
    if (listlen(line) > 1)
        line = "Owners: " + ((line.map('dbref)).to_english());
    else
        line = "Owner: " + ((line[1]).namef('xref));
    if (chop)
        line = line.chop(len);
    out = [@out, line];
    line = obj.parents();
    if (listlen(line) > 1)
        line = "Parents: " + ((line.map('dbref)).to_english());
    else
        line = "Parent: " + ((line[1]).namef('xref));
    if (chop)
        line = line.chop(len);
    out = [@out, line];
    out = [@out, "Location: " + ($object.get_name(.location(), 'namef, ['xref]))];
    return out;
.

method eval_offset
    return eval_offset || #[['mtime, 0], ['time, 0], ['ticks, 0]];
.