/
ColdCore-3.0a9.02/
ColdCore-3.0a9.02/src/
new object $programmer: $storyteller, $dmi_data_ui;

var $channel_ui active_channels = #[];
var $channel_ui channel_dict = #[];
var $command_aliases command_aliases = [];
var $described prose = [];
var $has_commands local = \
	#[["@id", [["@id", "*", "@id <any>", 'id_cmd, #[[1, ['any, []]]]]]],\
  ["@which",\
    [["@which", "*", "@which <any>", 'which_cmd, #[[1, ['any, []]]]]]],\
  ["@eval",\
    [["@eval", "*", "@eval <any>", 'eval_cmd, #[[1, ['any, []]]]]]],\
  ["@add-c?ommand|@ac",\
    [["@add-c?ommand|@ac", "*", "@add-c?ommand|@ac <any>", 'add_command_cmd, #[[1, ['any, []]]]]]],\
  ["@del-c?ommand|@dc",\
    [["@del-c?ommand|@dc", "*", "@del-c?ommand|@dc <any>", 'del_command_cmd, #[[1, ['any, []]]]]]],\
  ["@join",\
    [["@join", "*", "@join <any>", 'join_cmd, #[[1, ['any, []]]]]]],\
  ["@chpar?ents",\
    [["@chpar?ents", "*", "@chpar?ents <any>", 'chparents_cmd, #[[1, ['any, []]]]]]],\
  ["@add-s?hortcut|@as",\
    [["@add-s?hortcut|@as", "*", "@add-s?hortcut|@as <any>", 'add_shortcut_cmd, #[[1, ['any, []]]]]]],\
  ["@del-m?ethod|@delm?ethod|@dm",\
    [["@del-m?ethod|@delm?ethod|@dm", "*", "@del-m?ethod|@delm?ethod|@dm <objref>", 'del_method_cmd, #[[1, ['objref, []]]]]]],\
  ["@rehash",\
    [["@rehash", "", "@rehash", 'rehash_cmd, #[]]]], ["@trace-method|@trace", [["@trace-method|@trace", "*", "@trace-method|@trace <objref>", 'trace_method_cmd, #[[1, ['objref, []]]]]]],\
  ["@ledit",\
    [["@ledit", "*", "@ledit <objref: +e?dited>", 'local_edit_cmd, #[[1, ['objref_opt, ["e?dited"]]]]]]],\
  ["@program",\
    [["@program", "*", "@program <objref: +w?arnings +e?dited=1 +a?ccess=1 +f?lags=1>", 'program_cmd, #[[1, ['objref_opt, ["w?arnings", "e?dited", "a?ccess", "f?lags"]]]]], ["@program", "* with *", "@program <objref: +w?arnings +e?dited=1 +a?ccess=1 +f?lags=1> with <any>", 'program_cmd, #[[1, ['objref_opt, ["w?arnings", "e?dited", "a?ccess", "f?lags"]]]], [3, ['any, []]]]]], ["@del-v?ariable|@dv", [["@del-v?ariable|@dv", "*", "@del-v?ariable|@dv <objref>", 'del_var_cmd, #[[1, ['objref, []]]]]]],\
  ["@show",\
    [["@show", "*", "@show <objref: +c?hop>", 'show_cmd, #[[1, ['objref_opt, ["c?hop"]]]]]]],\
  ["@mv|@move|@cp|@copy",\
    [["@mv|@move|@cp|@copy", "*", "@mv|@move|@cp|@copy <objref:+c?omment=1>", 'move_cmd, #[[1, ['objref_opt, ["c?omment"]]]]]]],\
  ["@del-s?hortcut|@ds",\
    [["@del-s?hortcut|@ds", "*", "@del-s?hortcut|@ds <any>", 'del_shortcut_cmd, #[[1, ['any, []]]]]]],\
  ["@add-p?arent|@ap",\
    [["@add-p?arent|@ap", "*", "@add-p?arent|@ap <any>", 'add_parent_cmd, #[[1, ['any, []]]]]]],\
  ["@del-p?arent|@dp",\
    [["@del-p?arent|@dp", "*", "@del-p?arent|@dp <any>", 'del_parent_cmd, #[[1, ['any, []]]]]]],\
  ["@grep",\
    [["@grep", "*", "@grep <any:+d?escend +f?ull +l?ist +r?eplace-with=1>", 'grep_cmd, #[[1, ['any_opt, ["d?escend", "f?ull", "l?ist", "r?eplace-with"]]]]]]],\
  ["@chmod|@mmod|@omod|@chflag?s",\
    [["@chmod|@mmod|@omod|@chflag?s", "*", "@chmod|@mmod|@omod|@chflag?s <any>", 'chmod_cmd, #[[1, ['any, []]]]]]],\
  ["@dump",\
    [["@dump", "*", "@dump <any: +t?extdump +m?ethods +v?ariables +h?eader>", 'dump_cmd, #[[1, ['any_opt, ["t?extdump", "m?ethods", "v?ariables", "h?eader"]]]]]]],\
  ["@list",\
    [["@list", "*", "@list <objref: +n?umbers +t?extdump>", 'list_cmd, #[[1, ['objref_opt, ["n?umbers", "t?extdump"]]]]]]],\
  ["@add-v?ariable|@av",\
    [["@add-v?ariable|@av", "*", "@add-v?ariable|@av <any>", 'add_var_cmd, #[[1, ['any, []]]]]]],\
  ["@hl?ist|@help-list",\
    [["@hl?ist|@help-list", "*", "@hl?ist|@help-list <any>", 'help_list_cmd, #[[1, ['any, []]]]]]],\
  ["@hw?rite|@help-write",\
    [["@hw?rite|@help-write", "*", "@hw?rite|@help-write <any>", 'help_write_cmd, #[[1, ['any, []]]]]]],\
  ["@spawn",\
    [["@spawn", "*", "@spawn <any>", 'spawn_cmd, #[[1, ['any, []]]]]]],\
  ["@ancestors",\
    [["@ancestors", "*", "@ancestors <any>", 'ancestors_cmd, #[[1, ['any, []]]]]]],\
  ["@nh?n|@new-help|@new-help-node",\
    [["@nh?n|@new-help|@new-help-node", "*", "@nh?n|@new-help|@new-help-node <any:+n?amed=1 +o?bjname=1 +i?ndex=1>", 'new_help_node_cmd, #[[1, ['any_opt, ["n?amed", "o?bjname", "i?ndex"]]]]]]],\
  ["@config-set?ting|@configure-set?ting",\
    [["@config-set?ting|@configure-set?ting", "*", "@config-set?ting|@configure-set?ting <any>", 'configure_setting_cmd, #[[1, ['any, []]]]]]],\
  ["@def-set?ting|@define-set?ting",\
    [["@def-set?ting|@define-set?ting", "*", "@def-set?ting|@define-set?ting <any>", 'define_setting_cmd, #[[1, ['any, []]]]]]],\
  ["@undef-set?ting|@undefine-set?ting",\
    [["@undef-set?ting|@undefine-set?ting", "*", "@undef-set?ting|@undefine-set?ting <any>", 'undefine_setting_cmd, #[[1, ['any, []]]]]]],\
  ["@descend?ants",\
    [["@descend?ants", "*", "@descend?ants <objref:+a?ll +r?edundant +o?nly +n?ot>", 'descendants_cmd, #[[1, ['objref_opt, ["a?ll", "r?edundant", "o?nly", "n?ot"]]]]]]],\
  ["@d?isplay",\
    [["@d?isplay", "*", "@d?isplay <objref: +c?hop +g?enerations>", 'display_cmd, #[[1, ['objref_opt, ["c?hop", "g?enerations"]]]]]]],\
  ["@chman?age",\
    [["@chman?age", "*", "@chman?age <any>", 'chmanage_cmd, #[[1, ['any, []]]]]]]];
var $has_commands shortcuts = #[[";*", ['eval_cmd, ["eval", 1]]]];
var $has_name name = ['prop, "Generic Programmer", "Generic Programmer"];
var $located location = $body_cave;
var $located obvious = 1;
var $location contents = [];
var $mail_list last_letter = 0;
var $mail_list letters = #[];
var $mail_list letters_index = #[];
var $mail_list mail = [];
var $mail_list notify = [$programmer];
var $mail_list readers = [];
var $mail_list senders = 1;
var $mail_ui current = #[['location, 0], ['list, $programmer]];
var $mail_ui subscribed = #[[$programmer, [791485891, 0]]];
var $programmer eval_offset = 0;
var $programmer eval_prefix = 0;
var $programmer eval_tick_offset = 0;
var $root created_on = 796268969;
var $root defined_settings = #[["match-with", #[['parse, ['parse_match_with]]]], ["match-default", #[]], ["@program-options", #[]], ["@list-options", #[]]];
var $root flags = ['methods, 'code, 'core, 'command_cache, 'variables];
var $root inited = 1;
var $root managed = [$programmer];
var $root manager = $programmer;
var $root quota = 75000;
var $root settings = #[["match-default", "*"], ["home", $body_cave], ["@list-options", ""], ["@program-options", ""], ["match-with", 'match_pattern], ["extended-parsers", []]];
var $thing gender = $gender_neuter;
var $user connected_at = 0;
var $user connections = [];
var $user formatter = $plain_format;
var $user last_command_at = 0;
var $user modes = #[];
var $user parsers = [$command_parser];
var $user password = "*";
var $user task_connections = #[];

private method ._def_setcmd_opt() {
    arg opt, type, opts, dict;
    var i, value, m;
    
    if (!(i = opt in (opts.slice(1))))
        return dict;
    value = (opts[i])[4];
    if (!value)
        throw(~stop, ("No value for option \"" + opt) + "\".");
    catch any {
        switch (type) {
            case 'symbol:
                value = [(> value.to_symbol() <)];
            case 'data_list:
                value = (> fromliteral(value) <);
                if (type(value) != 'list)
                    value = [value];
        }
    } with {
        rethrow(~stop);
    }
    opt = opt.strip("?");
    if ((m = match_pattern(opt, "*-args"))) {
        opt = tosym(m[1]);
        if (!dict_contains(dict, opt))
            throw(~stop, ((("Arguments defined for " + opt) + " without defining ") + opt) + " method.");
        value = [(dict[opt])[1], @value];
        return dict_add(dict, opt, value);
    } else {
        return dict_add(dict, tosym(opt), value);
    }
};

protected method ._display_methods() {
    arg obj, info, chop, f;
    var type, types, line, out, m, len;
    
    len = .linelen();
    out = [];
    for type in (info.keys()) {
        line = (tostr(type).capitalize()) + " Methods";
        if (f)
            line += (" matching \"" + f) + "\"";
        out += [line + ":"];
        for m in (info[type]) {
            line = strfmt("%5l %4r %l.%l(%l)", $object_lib.parse_method_flags(m[8]), m[6], ((m[1]) != obj) ? (m[1]) : "", m[2], m[3]);
            if (chop)
                line = line.chop(len);
            out += [line];
            refresh();
        }
    }
    return out;
};

protected method ._display_variables() {
    arg obj, info, chop, f;
    var line, i, len, out, fmt;
    
    len = .linelen();
    line = "Object Variables";
    if (f)
        line += (" matching \"" + f) + "\"";
    out = [line];
    for i in (info.reverse()) {
        line = strfmt("  %s,%s: %d", ((i[1]) != obj) ? (i[1]) : "", i[2], i[3]);
        if (chop)
            line = line.chop(len);
        out += [line];
        refresh();
    }
    return out;
};

protected method ._edit_method_callback() {
    arg code, client_data;
    var errors, edited, object, method, warns, sender;
    
    (> .perms(caller(), $editor_reference) <);
    [object, method] = client_data;
    sender = this();
    code += [(("// $#Edited: " + ($time.format("%d %h %y %H:%M"))) + " ") + sender];
    errors = object.add_method(code, method);
    if (errors)
        return ['failure, errors];
    warns = (> $code_lib.verify_code(code, method, 1) <);
    return ['success, [warns + ["Method compiled."]]];
};

protected method ._list_method() {
    arg obj, method, @args;
    var code, opt, flags, f;
    
    [(args ?= 0), (opt ?= "")] = args;
    code = obj.list_method(method);
    flags = obj.method_flags(method);
    if (args) {
        code = code.numbered_text();
        return ([(((((("-- " + (obj.method_access(method))) + " method ") + obj) + ".") + method) + "()") + (flags ? (": " + (flags.join(", "))) : "")] + code) + ["--"];
    } else {
        return ([$object_lib.format_method_header(obj, method, opt, flags, obj.method_access(method))] + (code.prefix("  "))) + ["."];
    }
};

protected method ._move_method() {
    arg remove, fobj, fname, tobj, tname, comment;
    var code, line, result, access, flags;
    
    if ((fobj == tobj) && remove) {
        if ((| tobj.find_method(tname) |) == tobj)
            tobj.del_method(tname);
        return (> fobj.rename_method(fname, tname) <);
    }
    code = (> fobj.list_method(fname) <);
    flags = (> fobj.method_flags(fname) <);
    access = (> fobj.method_access(fname) <);
    if (comment) {
        line = (((((((("// $#" + (remove ? "Moved" : "Copied")) + " ") + ($time.format("%d %h %y %H:%M"))) + " from ") + fobj) + ".") + fname) + "() by ") + this();
        if (type(comment) == 'string)
            line += ": " + comment;
        code += [line];
    }
    if ((> tobj.add_method(code, tname) <))
        throw(~compile, "Error encountered upon moving method!");
    (> tobj.set_method_flags(tname, flags) <);
    (> tobj.set_method_access(tname, access) <);
    if (remove)
        (> fobj.del_method(fname) <);
};

public method ._move_variable() {
    arg remove, fobj, fname, tobj, tname, comment;
    var value, line, result, tmp;
    
    value = (> fobj.eval([("return " + fname) + ";"]) <);
    if ((value[1]) != 'result)
        throw(~eval, "An error was encountered upon evaluation.");
    value = value[2];
    (> tobj.add_var(tname, value) <);
    if (remove)
        (> fobj.del_var(fname) <);
};

public method ._show_methods() {
    arg obj, f, match, chop;
    var methods, types, m, t, out;
    
    types = #[];
    for m in (obj.methods()) {
        if (tostr(m).(match)(f) != 0)
            types = types.add_elem(obj.method_access(m), ((("." + m) + "(") + ((obj.method_info(m))[1])) + ")");
    }
    
    // hard-listing the types guarantee's their order
    out = [];
    for t in (['root, 'driver, 'public, 'protected, 'private, 'frob]) {
        if (!(types.contains(t)))
            continue;
        out += [(((tostr(t).capitalize()) + " methods matching \"") + f) + "\":"];
        for m in (types[t])
            out += ["  " + m];
    }
    return out;
};

public method ._show_variables() {
    arg obj, f, match, chop;
    var parent, out, v, line, len;
    
    out = [];
    len = .linelen();
    for parent in (obj.data()) {
        if (valid(parent[1])) {
            out += [(((parent[1]) + " variables matching \"") + f) + "\":"];
            if ((parent[1]).has_flag('variables, this())) {
                for v in (parent[2]) {
                    if (tostr(v[1]).(match)(f) == 0)
                        continue;
                    line = (("  " + (v[1])) + ": ") + toliteral(v[2]);
                    if (chop)
                        line = line.chop(len);
                    out += [line];
                }
            } else {
                out += ["  ** Permission Denied **"];
            }
        } else {
            out += [($object_lib.get_name(parent[1])) + " Variables:"];
            for v in (parent[2]) {
                if (tostr(v[1]).(match)(f) == 0)
                    continue;
                line = (("  " + (v[1])) + ": ") + toliteral(v[2]);
                if (chop)
                    line = line.chop(len);
                out += [line];
            }
        }
        refresh();
    }
    return out;
};

private method ._which_cmd() {
    arg partial, parent, type, more;
    var p, cmds, cmd, def, matches;
    
    cmds = (| parent.(type)() |) || #[];
    matches = [];
    for def in (cmds.keys()) {
        for cmd in (cmds[def]) {
            if (partial in ((cmd[3]).strip("?")))
                matches += [[more, cmd[3], cmd[4]]];
        }
    }
    return matches;
};

protected method .add_command_cmd() {
    arg cmdstr, cmd, str;
    var ref, t, args, objref;
    
    (> .perms(caller(), 'command) <);
    args = str.explode_quoted();
    if (listlen(args) > 2) {
        if ((args[2]) in ["to", "for"])
            args = delete(args, 2);
        t = delete(args, listlen(args)).join();
        objref = args.last();
    } else if (listlen(args) == 2) {
        t = args[1];
        objref = args[2];
    } else {
        return ("Syntax: `" + cmd) + " \"template\" [to|for] <objref>";
    }
    catch any {
        ref = (> $parse_lib.ref(objref) <);
        if ((ref[1]) != 'method)
            return ("The reference " + objref) + " is not for a method.";
        if ((!(ref[4])) || (!((ref[4]).valid_ident())))
            return ((("Invalid method name " + (ref[3])) + ".") + (ref[4])) + "().";
        (> (ref[2]).add_command(t, tosym(ref[4])) <);
    } with {
        return (traceback()[1])[2];
    }
    return strfmt("Command %d added to %s.%s()", t, ref[3], ref[4]);
};

protected method .add_parent_cmd() {
    arg cmdstr, cmd, args;
    var syn, obj, parent;
    
    (> .perms(caller(), 'command) <);
    args = args.explode();
    if ((listlen(args) > 2) && ((args[2]) == "to"))
        args = delete(args, 2);
    if (listlen(args) != 2)
        return ("Syntax: `" + cmd) + " <parent> [to] <object>`";
    parent = (> .match_env_nice(args[1]) <);
    obj = (> .match_env_nice(args[2]) <);
    catch any {
        (> obj.add_parent(parent) <);
        return ((("Added parent to " + (obj.namef('ref))) + ", parents: ") + ((obj.parents()).to_english())) + ".";
    } with {
        return (traceback()[1])[2];
    }
};

protected method .add_shortcut_cmd() {
    arg cmdstr, cmd, args;
    var ref, syn;
    
    (> .perms(caller(), 'command) <);
    args = args.explode_quoted();
    syn = cmd + " \"<shortcut>\" [to] \"<command>\" [on] \"<object>\"";
    if ((listlen(args) == 5) && (((args[2]) == "to") && ((args[4]) == "on")))
        args = [args[1], args[3], args[5]];
    if (listlen(args) != 3)
        return ("Syntax: `" + syn) + "`";
    ref = (> $parse_lib.ref(args[3]) <);
    if (((ref[1]) != 'method) || ((!(ref[4])) || (!(| tosym(ref[4]) |))))
        return ("Invalid method reference reference \"" + (args[3])) + "\".";
    catch any
        (> (ref[2]).add_shortcut(args[1], args[2], tosym(ref[4])) <);
    with
        return (traceback()[1])[2];
    return strfmt("Added shortcut %d to command %d on %s.%s().", args[1], args[2], ref[2], ref[4]);
};

protected method .add_var_cmd() {
    arg cmdstr, cmd, args;
    var ref, value;
    
    (> .perms(caller(), 'command) <);
    if (!args)
        return "Invalid obj,variable reference.";
    ref = (> $parse_lib.ref(args.word(1)) <);
    if (((ref[1]) != 'variable) || (!(ref[4])))
        return "Invalid obj,variable reference.";
    if (" " in args) {
        args = substr(args, (" " in args) + 1);
        if (args && ((args[1]) == "="))
            args = substr(args, (" " in args) + 1);
        if (args) {
            value = .eval([("return " + args) + ";"]);
            if ((value[1]) == 'errors)
                return ("Unable to parse value \"" + args) + "\".";
            value = value[2];
        } else {
            value = 0;
        }
    } else {
        value = 0;
    }
    catch any {
        (> (ref[3]).add_var(tosym(ref[4]), value) <);
    } with {
        if (error() in [~varexists, ~symbol])
            return (traceback()[1])[2];
        rethrow(error());
    }
    return ((((("Object variable " + (ref[3])) + ",") + (ref[4])) + " added with value ") + value) + ".";
};

protected method .ancestors_cmd() {
    arg cmdstr, cmd, args;
    var syn, obj, maxlevels, line;
    
    (> .perms(caller(), 'command) <);
    syn = cmd + " <obj> [<levels>]";
    args = args.explode();
    if (!((args.length()) in [1, 2]))
        return syn;
    obj = .match_env_nice(args[1]);
    if ((args.length()) == 2) {
        if ((args[2]) == "all")
            maxlevels = 0;
        else
            maxlevels = abs(toint(args[2])) + 1;
    } else {
        maxlevels = 3;
    }
    line = ("Ancestors of " + obj) + ":";
    if (maxlevels) {
        line += tostr(maxlevels - 1);
        line = ((line + " level") + (((maxlevels - 1) > 1) ? "s" : "s")) + ":";
    } else {
        line += "all levels:";
    }
    .tell(line);
    .tell(obj._display_ancestors("", #[], 0, maxlevels));
    .tell("---");
};

protected method .chmanage_cmd() {
    arg cmdstr, cmd, args;
    var obj, manager;
    
    (> .perms(caller(), 'command) <);
    args = (args.replace(" to ", " ")).explode();
    if ((!args) || ((args.length()) != 2))
        (> .tell_error(cmd + " <object> [to] <user>") <);
    obj = .match_env_nice(args[1]);
    manager = .match_env_nice(args[2]);
    if ((!(manager.is($user))) && (!(.is($admin))))
        return "Sorry you can only set users as managers.";
    catch any
        (> obj.change_manager(manager) <);
    with
        return (traceback()[1])[2];
    return ((("Manager on " + (obj.namef('xref))) + " changed to ") + (manager.namef('xref))) + ".";
};

protected method .chmod_cmd() {
    arg cmdstr, cmd, args;
    var a, ts, t, opts, b, objs, o, precedence, ref, flags, match, m;
    
    (> .perms(caller(), 'command) <);
    args = args.explode_quoted();
    ts = ["cod?e", "cor?e", "d?river", "fe?rtile", "fo?rked", "fr?ob", "l?ocked", "m?ethods", "na?tive", "no?override", "pri?vate", "pro?tected", "pu?blic", "r?oot", "v?ariables"];
    if (!args)
        return ("=> Syntax: `" + cmd) + " <options> <object> [<object ..]`";
    opts = #[];
    objs = [];
    for a in (args) {
        if (a && ((a[1]) in ["+", "-"])) {
            b = (a[1]) == "+";
            a = substr(a, 2);
            match = 0;
            for t in (ts) {
                if (match_template(a, t)) {
                    opts = dict_add(opts, tosym(t.strip()), b);
                    match++;
                    break;
                }
            }
            if (!match) {
                catch ~symbol
                    opts = dict_add(opts, tosym(a), b);
                with
                    .tell(("Invalid option '" + a) + "' (non-alphanumeric characters)");
            }
        } else {
            objs += [a];
        }
    }
    if (!objs)
        return [("=> Syntax: `" + cmd) + " <options> <object> [<object ..]`", "No objects specified."];
    if (!opts)
        return [("=> Syntax: `" + cmd) + " <options> <object> [<object ..]`", "No options specified."];
    
    // ok, now handle it, keep precedence for their own sake
    for o in (objs) {
        catch any {
            ref = (| $parse_lib.ref(o) |);
        } with {
            .tell((traceback()[1])[2]);
            continue;
        }
        if (!precedence) {
            precedence = ref[1];
        } else if ((ref[1]) != precedence) {
            .tell(((("Item '" + o) + "' is not a ") + precedence) + " reference.");
            .tell("All references must be the same type.");
            continue;
        }
        o = ref[3];
        for a in (dict_keys(opts)) {
            catch any {
                switch (a) {
                    case 'driver, 'private, 'protected, 'public, 'root, 'frob:
                        if (precedence != 'method) {
                            .tell((("Option " + ((opts[a]) ? "+" : "-")) + a) + " is only applicable to methods.");
                            continue;
                        }
                        m = (> tosym(ref[4]) <);
                        (> o.set_method_access(m, a) <);
                        .tell(((("Set " + ($parse_lib.buildref(@ref))) + " access to ") + a) + ".");
                    case 'nooverride, 'locked, 'native, 'forked:
                        if (precedence != 'method) {
                            .tell((("Option " + ((opts[a]) ? "+" : "-")) + a) + " is only applicable to methods.");
                            continue;
                        }
                        m = (> tosym(ref[4]) <);
                        if (opts[a]) {
                            o.set_method_flags(m, setadd(o.method_flags(m), a));
                            .tell((((("Added Method Flag +" + a) + " to ") + ($parse_lib.buildref(@ref))) + ", flags: ") + (((o.method_flags(m)).prefix("+")).join()));
                        } else {
                            o.set_method_flags(m, setremove(o.method_flags(m), a));
                            .tell((((("Removed Method Flag +" + a) + " from ") + ($parse_lib.buildref(@ref))) + ", flags: ") + (((o.method_flags(m)).prefix("+")).join()));
                        }
                    default:
                        if (precedence != 'object) {
                            .tell((("Option " + ((opts[a]) ? "+" : "-")) + a) + " is only applicable to objects.");
                            continue;
                        }
                        if (opts[a]) {
                            o.add_flag(a);
                            .tell((((("Added Object Flag +" + a) + " to ") + (o.namef('ref))) + ", flags: ") + (((o.flags()).prefix("+")).join()));
                        } else {
                            o.del_flag(a);
                            .tell((((("Removed Object Flag +" + a) + " from ") + (o.namef('ref))) + ", flags: ") + (((o.flags()).prefix("+")).join()));
                        }
                }
            } with {
                .tell((traceback()[1])[2]);
            }
        }
        refresh();
    }
};

protected method .chparents_cmd() {
    arg cmdstr, cmd, args;
    var syn, p, x, obj, parents, match;
    
    (> .perms(caller(), 'command) <);
    syn = ("Syntax: `" + cmd) + " <child> [to] <parent>, <parent>, ...`";
    if ((match = match_template(args, "* to *"))) {
        obj = match[1];
        parents = match[3];
    } else if ((args = explode(args))) {
        if (listlen(args) == 1)
            return syn;
        obj = args[1];
        parents = sublist(args, 2).join();
    } else {
        return syn;
    }
    obj = (> .match_env_nice(obj) <);
    if (("," in parents) || (" and " in parents))
        parents = parents.explode_english_list();
    else
        parents = parents.explode();
    if (!parents)
        return "No parents to change to.";
    parents = map p in (parents) to ((> .match_env_nice(p) <));
    catch any {
        obj.chparents(@parents);
        return ((("Changed parents for " + obj) + " to ") + (parents.to_english())) + ".";
    } with {
        return (traceback()[1])[2];
    }
};

public method .clear_eval() {
    (| clear_var('eval_offset) |);
};

public method .configure_setting_cmd() {
    arg cmdstr, cmd, args;
    var def_opts, opts, o, name, config, m, definer, syn, type, def, val;
    
    syn = ("Syntax: `" + cmd) + " <definer>:<setting>[=default] [options]`";
    def_opts = [];
    for o in (["get", "set", "parse"])
        def_opts += [[o, 1], [o + "-a?rgs", 1]];
    def_opts += [["c?lear", 1], ["f?ormat", 1], ["a?ccess", 1], ["t?ype", 1]];
    [args, opts] = $parse_lib.getopt(args, def_opts);
    args = join(args, " ");
    if (!args) {
        .tell(syn);
        for o in (def_opts.slice(1))
            .tell(((("   +" + o) + "=<") + ((o.strip("?")).replace("-", " "))) + ">");
        return "Types can be any ColdC type and \"boolean\"";
    }
    if ((m = regexp(args, "^([^=]+) *= *(.*)$")))
        [args, def] = m;
    else
        def = "";
    if ((m = regexp(args, "^([^:]+) *: *([\@a-z0-9-]+)")))
        [definer, name] = m;
    else
        definer = args;
    catch any
        definer = (> .match_environment(definer) <);
    with
        return (traceback()[1])[2];
    if (!name)
        return syn;
    catch ~setnf
        definer = definer.setting_definer(name);
    with
        return (traceback()[1])[2];
    
    // setup some default config opts based off the desired type
    config = #[];
    if ((m = "t?ype" in (opts.slice(1)))) {
        type = (| ((opts[m])[4]).to_symbol() |);
        if ((!type) || (!($settings.is_valid_type(type))))
            return "Types can be any ColdC type and " + (($settings.valid_types()).to_english("", " or "));
        switch (type) {
            case 'boolean:
                config = #[['parse, ['is_boolean]], ['format, ['format_boolean]]];
            case 'itemlist:
                // do nothing, we re-adjust things later
            default:
                config = #[['parse, ['is_type, type]]];
        }
    }
    
    // now build default config--not the most efficient way--but cleaner
    config = (> ._def_setcmd_opt("get", 'symbol, opts, config) <);
    config = (> ._def_setcmd_opt("get-a?rgs", 'data_list, opts, config) <);
    config = (> ._def_setcmd_opt("set", 'symbol, opts, config) <);
    config = (> ._def_setcmd_opt("set-a?rgs", 'data_list, opts, config) <);
    config = (> ._def_setcmd_opt("parse", 'symbol, opts, config) <);
    config = (> ._def_setcmd_opt("parse-a?rgs", 'data_list, opts, config) <);
    config = (> ._def_setcmd_opt("c?lear", 'symbol, opts, config) <);
    config = (> ._def_setcmd_opt("f?ormat", 'symbol, opts, config) <);
    config = (> ._def_setcmd_opt("a?ccess", 'symbol, opts, config) <);
    if (type == 'itemlist) {
        config = config.add('parse, ['parse_itemlist, @(| config['parse] |) || []]);
        if (!(config.contains('format)))
            config = config.add('format, ['format_itemlist]);
    }
    
    // now reconfig it
    for o in (config) {
        catch any
            (> definer.set_setting_attr(name, @o) <);
        with
            .tell((traceback()[1])[2]);
    }
    .tell(((("Reconfigured setting " + definer) + ":") + name) + " as:");
    config = (definer.defined_settings())[name];
    for o in (config) {
        val = o[2];
        o = o[1];
        .tell((("    +" + strsub(tostr(o), "_", "-")) + "=") + (val[1]));
        if (listlen(val) > 1)
            .tell((("    +" + strsub(tostr(o), "_", "-")) + "-args=") + (map m in (sublist(val, 2)) to (toliteral(m)).join(",")));
    }
};

protected method .create_cmd() {
    arg cmdstr, cmd, args;
    var new, parents, obj;
    
    (> .perms(caller(), 'command) <);
    args = (args.replace(" from ", " ")).explode();
    if ((!args) || ((args.length()) < 2))
        .tell_error(cmd + " <object> [from] <parent>[, <parent> ...]");
    new = args[1];
    parents = [];
    for obj in (args.subrange(2))
        parents += [.match_env_nice(obj)];
};

public method .define_setting_cmd() {
    arg cmdstr, cmd, args;
    var def_opts, opts, o, name, config, m, definer, syn, type, def, val;
    
    syn = ("Syntax: `" + cmd) + " <definer>:<setting>[=default] [options]`";
    def_opts = [];
    for o in (["get", "set", "parse"])
        def_opts += [[o, 1], [o + "-a?rgs", 1]];
    def_opts += [["c?lear", 1], ["f?ormat", 1], ["a?ccess", 1], ["t?ype", 1]];
    [args, opts] = $parse_lib.getopt(args, def_opts);
    args = join(args, " ");
    if (!args) {
        .tell(syn);
        for o in (def_opts.slice(1))
            .tell(((("   +" + o) + "=<") + ((o.strip("?")).replace("-", " "))) + ">");
        return "Types can be any ColdC type and \"boolean\"";
    }
    if ((m = regexp(args, "^([^=]+) *= *(.*)$")))
        [args, def] = m;
    else
        def = "";
    if ((m = regexp(args, "^([^:]+) *: *([\@a-z0-9-]+)")))
        [definer, name] = m;
    else
        return syn;
    catch any
        definer = (> .match_environment(definer) <);
    with
        return (traceback()[1])[2];
    if (!name)
        return syn;
    
    // setup some default config opts based off the desired type
    config = #[];
    if ((m = "t?ype" in (opts.slice(1)))) {
        type = (| ((opts[m])[4]).to_symbol() |);
        if ((!type) || (!($settings.is_valid_type(type))))
            return "Types can be any ColdC type and " + (($settings.valid_types()).to_english("", " or "));
        switch (type) {
            case 'boolean:
                config = #[['parse, ['is_boolean]], ['format, ['format_boolean]]];
            case 'itemlist:
                // do nothing, we re-adjust things later
            default:
                config = #[['parse, ['is_type, type]]];
        }
    }
    
    // now build default config--not the most efficient way--but cleaner
    config = (> ._def_setcmd_opt("get", 'symbol, opts, config) <);
    config = (> ._def_setcmd_opt("get-a?rgs", 'data_list, opts, config) <);
    config = (> ._def_setcmd_opt("set", 'symbol, opts, config) <);
    config = (> ._def_setcmd_opt("set-a?rgs", 'data_list, opts, config) <);
    config = (> ._def_setcmd_opt("parse", 'symbol, opts, config) <);
    config = (> ._def_setcmd_opt("parse-a?rgs", 'data_list, opts, config) <);
    config = (> ._def_setcmd_opt("c?lear", 'symbol, opts, config) <);
    config = (> ._def_setcmd_opt("f?ormat", 'symbol, opts, config) <);
    config = (> ._def_setcmd_opt("a?ccess", 'symbol, opts, config) <);
    if (type == 'itemlist) {
        config = config.add('parse, ['parse_itemlist, @(| config['parse] |) || []]);
        if (!(config.contains('format)))
            config = config.add('format, ['format_itemlist]);
    }
    
    // now add it..
    catch any
        config = (> definer.define_setting(name, config) <);
    with
        return (traceback()[1])[2];
    .tell(((("-- Defined setting " + definer) + ":") + name) + " as:");
    for o in (config) {
        val = o[2];
        o = o[1];
        .tell((("    +" + strsub(tostr(o), "_", "-")) + "=") + (val[1]));
        if (listlen(val) > 1)
            .tell((("    +" + strsub(tostr(o), "_", "-")) + "-args=") + (sublist(val, 2).join(",")));
    }
    
    // and set the default value
    catch any {
        o = definer;
        o = o.set_setting(name, o, def);
        val = o.format_setting(name, o, o.get_setting(name, o));
        return ["-- Default Setting:", (("  " + name) + " = ") + val, "--"];
    } with {
        return (traceback()[1])[2];
    }
};

protected method .del_command_cmd() {
    arg cmdstr, cmd, args;
    var ref, t, objref;
    
    (> .perms(caller(), 'command) <);
    args = args.explode_quoted();
    if (listlen(args) > 2) {
        if ((args[2]) == "from")
            args = delete(args, 2);
        t = delete(args, listlen(args)).join();
        objref = args.last();
    } else if (listlen(args) == 2) {
        t = args[1];
        objref = args[2];
    } else {
        return ("Syntax: `" + cmd) + " \"template\" [from] <objref>";
    }
    catch any {
        ref = (> $parse_lib.ref(objref) <);
        if ((ref[1]) != 'method)
            return ("The reference " + objref) + " is not for a method.";
        if ((!(ref[4])) || (!((ref[4]).valid_ident())))
            return ((("Invalid method name " + (ref[3])) + ".") + (ref[4])) + "().";
        if (!(> (ref[2]).del_command(t, tosym(ref[4])) <))
            return strfmt("Command %d is not defined on %s.", t, ref[2]);
    } with {
        return (traceback()[1])[2];
    }
    return strfmt("Command %d removed from %s.%s()", t, ref[3], ref[4]);
};

protected method .del_method_cmd() {
    arg cmdstr, cmd, objref;
    var name, obj;
    
    (> .perms(caller(), 'command) <);
    if (!(objref[4]))
        return .tell(("No method specified to delete from " + (objref[3])) + ".");
    if (!(| (name = tosym(objref[4])) |))
        return .tell(("Invalid method name \"" + (objref[4])) + "\".");
    catch any {
        (> (objref[3]).del_method(name) <);
        .tell(((("Method " + (objref[3])) + ".") + name) + "() deleted.");
    } with {
        if (error() == ~methodnf)
            .tell(((("Method " + (objref[3])) + ".") + name) + "() does not exist.");
        else
            .tell((traceback()[1])[2]);
    }
};

protected method .del_parent_cmd() {
    arg cmdstr, cmd, args;
    var syn, obj, parent;
    
    (> .perms(caller(), 'command) <);
    args = args.explode();
    if ((listlen(args) > 2) && ((args[2]) == "from"))
        args = delete(args, 2);
    if (listlen(args) != 2)
        return ("Syntax: `" + cmd) + " <parent> [from] <object>`";
    if (!args)
        .tell_error(syn);
    parent = (> .match_env_nice(args[1]) <);
    obj = (> .match_env_nice(args[2]) <);
    catch any {
        (> obj.del_parent(parent) <);
        return ((("Deleted parent from " + (obj.namef('ref))) + ", parents: ") + ((obj.parents()).to_english())) + ".";
    } with {
        return (traceback()[1])[2];
    }
};

protected method .del_shortcut_cmd() {
    arg cmdstr, cmd, args;
    var ref, syn;
    
    (> .perms(caller(), 'command) <);
    args = args.explode_quoted();
    if ((listlen(args) == 3) && ((args[2]) == "from"))
        args = delete(args, 2);
    if (listlen(args) != 2)
        return ("Syntax: `" + cmd) + " \"<shortcut>\" [from] <objref>";
    ref = (> $parse_lib.ref(args[2]) <);
    if (((ref[1]) != 'method) || ((!(ref[4])) || (!(| tosym(ref[4]) |))))
        return ("Invalid method reference reference \"" + (args[3])) + "\".";
    catch any
        (> (ref[2]).del_shortcut(args[1]) <);
    with
        return (traceback()[1])[2];
    return strfmt("Deleted shortcut %d from %s.%s().", args[1], ref[2], ref[4]);
};

protected method .del_var_cmd() {
    arg cmdstr, cmd, ref;
    
    (> .perms(caller(), 'command) <);
    if (((ref[1]) != 'variable) || (!(ref[4])))
        return "Invalid obj,variable reference.";
    catch ~symbol
        (ref[3]).del_var(tosym(ref[4]));
    with
        return (traceback()[1])[2];
    return ((("Object variable " + (ref[3])) + ",") + (ref[4])) + " deleted.";
};

protected method .descendants_cmd() {
    arg cmdstr, cmd, args;
    var obj, max, line, opts, i, r, not, only, f;
    
    (> .perms(caller(), 'command) <);
    
    // parse args
    [obj, args, opts] = args;
    if ((obj[1]) != 'object)
        return "Object reference must simply be a dbref.";
    if ((obj[2]) != (obj[3]))
        .tell(("Ignoring specified definer " + (obj[2])) + ".");
    obj = obj[3];
    only = (not = []);
    max = 1;
    if ((i = "o?nly" in (opts.slice(1)))) {
        only = map f in (((opts[i])[4]).split(" *, *")) to (f.to_symbol());
        opts = delete(opts, i);
    }
    if ((i = "n?ot" in (opts.slice(1)))) {
        not = map f in (((opts[i])[4]).split(" *, *")) to (f.to_symbol());
        opts = delete(opts, i);
    }
    if ((i = "r?edundant" in (opts.slice(1)))) {
        r = (opts[i])[3];
        opts = delete(opts, i);
    }
    if ((i = "a?ll" in (opts.slice(1)))) {
        if ((opts[i])[3])
            max = 0;
        opts = delete(opts, i);
    }
    if (opts) {
        if ((max = find i in (opts) where ((i[2]).is_numeric()))) {
            max = toint((opts[max])[2]);
            if (max < 1)
                return "Maximum levels must be greater than zero.";
        }
    }
    
    // do it
    line = ("-- Descendants of " + obj) + " [";
    .tell((line + (((obj.parents()).mmap('objname)).to_english())) + "]");
    if (max) {
        line = (max + " level") + ((max > 1) ? "s" : "");
        max++;
    } else {
        line = "all levels";
    }
    if (r)
        line += ", redundant entries";
    .tell("-- " + line);
    if (only)
        .tell("-- only objects with flag(s): +" + (only.join(" +")));
    if (not)
        .tell("-- not objects with flag(s): +" + (not.join(" +")));
    return [obj.format_descendants("", #[], 0, max, only, not, r), "--"];
};

protected method .display_cmd() {
    arg cmdstr, cmd, args;
    var opts, slice, what, match, i, chop, f, gen, def, obj, out;
    
    (> .perms(caller(), 'command) <);
    opts = args[3];
    args = args[1];
    chop = 1;
    slice = opts.slice(1);
    if ((i = "c?hop" in slice) && (!((opts[i])[3])))
        chop = 0;
    else
        chop = .linelen();
    def = args[3];
    if ((i = "g?enerations" in slice)) {
        gen = (opts[i])[4];
        if (gen.is_numeric())
            gen = ['generations, toint(gen)];
        else if (gen)
            gen = ['ancestors_descending, (> .match_env_nice(gen) <)];
        else
            gen = ['ancestors_to, def];
        def = 0;
    } else {
        gen = ['generations, 1];
    }
    what = [args[1]] + ((| args[5] |) ? [args[5]] : []);
    obj = args[2];
    if (type(obj) == 'frob)
        return ["The target object was a frob.  Please use @exam instead."];
    out = $object_lib.format_object(obj, chop);
    if (!(args[4]))
        f = .get_setting("match-default", $programmer);
    else
        f = args[4];
    match = .get_setting("match-with", $programmer);
    if ('method in what)
        out += ._display_methods(obj, obj.list_methods(gen, def, f, match), chop, f);
    if ('variable in what)
        out += ._display_variables(obj, obj.variable_info(gen, def, f, match), chop, f);
    return out + ["---"];
};

protected method .dump_cmd() {
    arg cmdstr, cmd, args;
    var opts, objs, o, i, tdfmt, meths, vars, header;
    
    (> .perms(caller(), 'command) <);
    opts = args[2];
    args = args[1];
    o = opts.slice(1);
    (i = "t?extdump" in o) && (tdfmt = (opts[i])[3]);
    (i = "m?ethods" in o) ? (meths = (opts[i])[3]) : (meths = 1);
    (i = "v?ariables" in o) ? (vars = (opts[i])[3]) : (vars = 1);
    (i = "h?eader" in o) ? (header = (opts[i])[3]) : (header = 1);
    if ((!meths) && (!vars))
        return "Perhaps you will want to dump methods and/or vars next time?";
    objs = [];
    for o in (args) {
        catch any
            objs += [(> .match_env_nice(o) <)];
        with
            .tell((traceback()[1])[2]);
    }
    if (!objs)
        return "Dump nothing?";
    if (tdfmt)
        .dump_fmt_textdump(objs, meths, vars, header);
    else
        .dump_fmt_commands(objs, meths, vars, header);
};

protected method .dump_fmt_commands() {
    arg objs, meths, vars, header;
    var data, obj, out, a, v, m, line, pars, cmdopts;
    
    // this uses .tell() to keep its internal overhead from bloating
    // it could be faster by building a list and printing it all at once
    // but this is nicer on the server (especially when dumping large objects).
    for obj in (objs) {
        refresh();
        if (header) {
            pars = obj.parents();
            line = (((((((";var p, new; if(!(| valid(" + obj) + ") |)) ") + "{ new = ") + (pars[1])) + ".spawn();") + " new.set_objname('") + (obj.objname())) + ");}";
            if (listlen(pars) > 1)
                line += (" obj.chparents(" + join(pars, ",")) + ");";
            .tell(line);
        }
        if (vars) {
            catch ~perm {
                data = (> obj.data() <);
                for a in (dict_keys(data)) {
                    refresh();
                    for v in (data[a]) {
                        if (a == obj)
                            .tell(strfmt("@av %l,%l = %d", obj, @v));
                        .tell(strfmt(";|as %l<%l>;%l = %d;", obj, a, @v));
                    }
                }
            } with {
                .tell((traceback()[1])[2]);
            }
        }
        if (meths) {
            cmdopts = .get_setting("@program-options", $programmer);
            catch ~perm {
                for m in ((> obj.methods() <)) {
                    refresh();
                    .tell(.format_method(obj, m, 'normal, cmdopts));
                }
            } with {
                .tell((traceback()[1])[2]);
            }
        }
    }
};

protected method .dump_fmt_textdump() {
    arg objs, meths, vars, header;
    var data, obj, out, a, v, m;
    
    // this uses .tell() to keep its internal overhead from bloating
    // it could be faster by building a list and printing it all at once
    // but this is nicer on the server (especially when dumping large objects).
    for obj in (objs) {
        refresh();
        if (header)
            .tell([((("object " + obj) + ": ") + ((obj.parents()).join(", "))) + ";", ""]);
        if (vars) {
            catch ~perm {
                data = (> obj.data() <);
                for a in (dict_keys(data)) {
                    refresh();
                    for v in (data[a])
                        .tell(strfmt("var %l %l = %d;", a, @v));
                }
            } with {
                .tell((traceback()[1])[2]);
            }
        }
        .tell("");
        if (meths) {
            catch ~perm {
                for m in ((> obj.methods() <)) {
                    refresh();
                    .tell([""] + (.format_method(obj, m, 'textdump)));
                }
            } with {
                .tell((traceback()[1])[2]);
            }
        }
    }
};

protected method .eval_cmd() {
    arg cmdstr, com, str;
    var result, adjust, vars, v, evalp, times, line, reg, obj, definer, ref, debug;
    
    (> .perms(caller(), 'command) <);
    evalp = .eval_prefix();
    vars = (evalp.keys()).join(", ");
    v = (evalp.values()).join();
    
    // clean it up
    str = strsed(str, "^;*", "");
    
    // perform escape substitution
    if (str && ((str[1]) == "|"))
        str = substr(str, 2);
    else
        str = .eval_subs(str);
    
    // check for debug flags
    if ((reg = regexp(str, "^(trace|debug|profile) *;*(.*)$"))) {
        [debug, str] = reg;
        debug = #[["trace", 'trace], ["debug", 'debug], ["profile", 'profile]][debug];
    } else {
        debug = 0;
    }
    
    // who are we evaluating as
    if ((reg = regexp(str, "^ *as +([^; ]+)"))) {
        ref = $parse_lib.ref(reg[1]);
        obj = ref[2];
        definer = ref[3];
        str = strsed(str, "^ *as +([^; ]+)[ ;]+", "");
        if ((!(definer.is_writable_by(this()))) || (!(obj.is_writable_by(this()))))
            return ("You do not have permission to evaluate on " + (reg[1])) + ".";
        if (!(obj.is(definer)))
            return (obj + " isn't a child of ") + definer;
    } else {
        obj = (definer = this());
    }
    
    // are we just adjusting our offset?
    if (!str) {
        result = (> .evaluate(((("var " + vars) + ";") + v) + "return (> 1 <);", obj, definer, 'no_offset) <);
        result = replace(result[1], 1, ((result[1])[1]) - 1);
        if (eval_offset)
            line = strfmt("adjusted by %s ticks and %s.%6{0}r seconds.", (eval_offset[1]) - (result[1]), (eval_offset[2]) - (result[2]), abs((eval_offset[3]) - (result[3])));
        else
            line = strfmt("set to %s ticks and %s.%6{0}r seconds.", @result);
        eval_offset = result;
        return "Eval offset " + line;
    }
    
    // format it, use heuristics
    if (match_begin(str, "var") && (reg = regexp(str, "var ([^;]+)"))) {
        str = strsed(str, "var ([^;]+);", "");
        str = ((((("var " + vars) + ", ") + (reg.join(","))) + ";") + v) + str;
    } else if ("return" in str) {
        str = ((("var " + vars) + ";") + v) + str;
    } else {
        str = strsed(str, " *;* *$", "");
        str = ((((("var " + vars) + ";") + v) + "return (> ") + str) + " <);";
    }
    if (debug)
        [times, result, debug] = (> .evaluate(str, obj, definer, debug) <);
    else
        [times, result] = (> .evaluate(str, obj, definer) <);
    
    // Display the errors, or the result.
    if ((result[1]) == 'errors) {
        .tell(result[2]);
    } else if ((result[1]) == 'traceback) {
        .tell_traceback(result[2]);
        line = strfmt("[ seconds: %l.%6{0}r; operations: %s", times[2], times[3], times[1]);
        if (times[2])
            line += (" (" + ((times[1]) / (times[2]))) + " ticks per second)";
        return line + " ]";
    } else {
        if (type(result[2]) == 'objnum)
            .tell("=> " + ((| (result[2]).namef('xref) |) || (result[2])));
        else
            .tell("=> " + toliteral(result[2]));
        if (debug)
            .tell(debug);
        line = strfmt("[ seconds: %l.%6{0}r; operations: %s", times[2], times[3], times[1]);
        if (times[2])
            line += (" (" + ((times[1]) / (times[2]))) + " ticks per second)";
        return line + " ]";
    }
};

protected method .eval_offset() {
    return eval_offset || #[['mtime, 0], ['time, 0], ['ticks, 0]];
};

public method .eval_prefix() {
    return #[["me", ("me = " + this()) + ";"], ["here", ("here = " + (.location())) + ";"]].union(eval_prefix || #[]);
};

protected 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 == (code.length())) || ((code.subrange(idx + 1, 1)) == "^")) {
            ret_code += code.subrange(1, idx);
            code = code.subrange(idx + 1);
            if (code && ((code[1]) == "^"))
                code = code.subrange(2);
        } else {
            if (idx > 1) {
                ret_code += code.subrange(1, idx - 1);
                code = code.subrange(idx + 1);
            } else {
                code = code.subrange(2);
            }
            idx = 1;
            while ((idx <= (code.length())) && (!((code[idx]) in " =.()[]=<>?|&!*+-/';\"")))
                idx++;
            sub = .match_env_nice(code.subrange(1, idx - 1));
            ret_code += sub;
            code = code.subrange(idx);
        }
    }
    return ret_code;
};

public method .evaluate() {
    arg str, obj, definer, @mode;
    var start, end, time, ticks, mtime, times1, times2, method, errs, trace, result, is_error;
    
    mode = mode ? (mode[1]) : 0;
    if (sender() != $eval_parser)
        (> .perms(caller(), $programmer) <);
    method = tosym("tmp_eval_" + time());
    if ((errs = (> definer.add_method([str], method, 'evalonly) <))) {
        if (mode)
            return [[0, 0, 0], ['errors, errs, 0, 0], []];
        else
            return [[0, 0, 0], ['errors, errs, 0, 0]];
    }
    catch any {
        if (mode in ['trace, 'profile])
            debug_callers(1);
        else if (mode == 'debug)
            debug_callers(2);
        times1 = [tick(), time(), mtime()];
        result = (> obj.(method)() <);
        times2 = [mtime(), time(), tick()];
        trace = call_trace();
        debug_callers(0);
    } with {
        times2 = [mtime(), time(), tick()];
        result = traceback();
        is_error = 1;
        debug_callers(0);
    }
    (| definer.del_method(method) |);
    
    // figure up the actual times
    time = (times2[2]) - (times1[2]);
    ticks = (times2[3]) - (times1[1]);
    if ((times2[1]) > (times1[3]))
        mtime = (times2[1]) - (times1[3]);
    else if (time)
        mtime = ((time * 1000000) + (1000000 - (times1[3]))) + (times2[1]);
    else
        mtime = (1000000 - (times2[1])) + (times1[3]);
    
    // offset it?
    if (eval_offset && (mode != 'no_offset)) {
        ticks -= eval_offset[1];
        time -= eval_offset[2];
        mtime -= eval_offset[3];
    }
    if (trace)
        return [[ticks, time, abs(mtime)], ['result, result], $code_lib.generate_debug_listing(trace, mode)];
    return [[ticks, time, abs(mtime)], [is_error ? 'traceback : 'result, result]];
};

protected method .format_method() {
    arg obj, method, format, @opts;
    var code, opt, flags, f;
    
    // this needs to be on $programmer ot get the programmers perms
    [(opt ?= "")] = opts;
    code = obj.list_method(method);
    flags = obj.method_flags(method);
    switch (format) {
        case 'textdump:
            return ([(((((((obj.method_access(method)) + " method ") + obj) + ".") + method) + "()") + (flags ? (": " + (flags.join(", "))) : "")) + " {"] + (code.prefix("    "))) + ["};"];
        case 'numbered:
            code = code.numbered_text();
            return ([(((((("-- " + (obj.method_access(method))) + " method ") + obj) + ".") + method) + "()") + (flags ? (": " + (flags.join(", "))) : "")] + code) + ["--"];
        default:
            return ([$object_lib.format_method_header(obj, method, opt, flags, obj.method_access(method))] + (code.prefix("  "))) + ["."];
    }
};

protected method .grep_brief() {
    arg regexp, objs;
    var obj, method, out, x, l, line, lines, code;
    
    for obj in (objs) {
        if (!valid(obj))
            continue;
        if (!(obj.has_flag('code))) {
            .tell(("You cannot read method code on " + obj) + ", skipping..");
            continue;
        }
        out = [];
        for method in (obj.methods()) {
            code = obj.list_method(method);
            lines = [];
            for x in [1 .. listlen(code)] {
                l = code[x];
                if (match_regexp(l, regexp))
                    lines += [x];
                refresh();
            }
            if (lines)
                out += [(((obj + ".") + method) + "(): ") + (lines.to_english())];
            refresh();
        }
        if (out)
            .tell(out);
        refresh();
    }
    return "---";
};

protected method .grep_cmd() {
    arg cmdstr, cmd, args;
    var more, regexp, from, syn, opts, d, f, l, r, rep, slice, objs, obj, out;
    
    (> .perms(caller(), 'command) <);
    [more, opts] = args;
    if ((more.length()) < 2)
        return ("=> Syntax: `" + cmd) + " [options] <regexp> <object> <object>..";
    regexp = more[1];
    more = more.subrange(2);
    
    // handle the options
    slice = opts.slice(1);
    if ((r = (| "r?eplace-with" in slice |))) {
        rep = (opts[r])[4];
        r = (opts[r])[3];
    }
    if ((d = (| "d?escend" in ((args[2]).slice(1)) |)))
        d = (opts[d])[3];
    if ((l = (| "l?ist" in ((args[2]).slice(1)) |)))
        l = (opts[l])[3];
    if ((f = (| "f?ull" in ((args[2]).slice(1)) |)))
        f = (opts[f])[3];
    
    // now we check for conflicting or incorrect options..
    if (d && (!(.is($admin))))
        return "Only administrators may use the +descend option, talk to one.";
    if (d && ((more.length()) > 1))
        return "+descend can only be used with a single object as the target.";
    if (r && (f || l))
        return "+replace-with option cannot be used with +full or +list.";
    if (f && l)
        return "+full cannot be used with +list.";
    
    // the pause() flushes so we can see the 'Searching for ..'
    // Do this now because .descendants() can lag
    .tell(("Searching for \"" + regexp) + "\"...");
    pause();
    
    // figure out our targets
    if (d) {
        obj = (> .match_env_nice(more[1]) <);
        objs = [obj, @obj.descendants()];
    } else {
        objs = [];
        for obj in (more)
            objs += [(> .match_env_nice(obj) <)];
    }
    
    // call the right grep method
    if (r)
        return (> .grep_replace(regexp, objs, rep) <);
    else if (l)
        return (> .grep_list(regexp, objs) <);
    else if (f)
        return (> .grep_full(regexp, objs) <);
    else
        return (> .grep_brief(regexp, objs) <);
};

protected method .grep_full() {
    arg regexp, objs;
    var obj, method, out, x, l, code;
    
    for obj in (objs) {
        if (!valid(obj))
            continue;
        if (!(obj.has_flag('code))) {
            .tell(("You cannot read method code on " + obj) + ", skipping..");
            continue;
        }
        out = [];
        for method in (obj.methods()) {
            code = obj.list_method(method);
            for x in [1 .. listlen(code)] {
                l = code[x];
                if (match_regexp(l, regexp))
                    out += [(((((obj + ".") + method) + "() line ") + x) + ": ") + l];
                refresh();
            }
            refresh();
        }
        if (out)
            .tell(out);
        refresh();
    }
    return "---";
};

protected method .grep_list() {
    arg regexp, objs;
    var obj, code, x, l, lr, what, loop, method, opt;
    
    opt = .get_setting("@program-options", $programmer);
    for obj in (objs) {
        if (!valid(obj))
            continue;
        if (!(obj.has_flag('code))) {
            .tell(("You cannot read method code on " + obj) + ", skipping..");
            continue;
        }
        for method in (obj.methods()) {
            code = obj.list_method(method);
            for l in (code) {
                if (match_regexp(l, regexp)) {
                    .tell(([$object_lib.format_method_header(obj, method, opt, obj.method_flags(method), obj.method_access(method))] + (code.prefix("  "))) + ["."]);
                    break;
                }
                refresh();
            }
            refresh();
        }
        refresh();
    }
    return "---";
};

protected method .grep_replace() {
    arg regexp, objs, replace;
    var obj, method;
    
    for obj in (objs) {
        if (!valid(obj))
            continue;
        if (!(obj.is_writable_by(this()))) {
            .tell(("You cannot write on " + obj) + ", skipping..");
            continue;
        }
        for method in (obj.methods()) {
            (> .grep_replace_method(obj, method, regexp, replace) <);
            refresh();
        }
        refresh();
    }
    return "Done.";
};

protected method .grep_replace_method() {
    arg obj, method, regexp, replace;
    var old_code, code, x, l, lr, errs, what;
    
    old_code = (code = obj.list_method(method));
    for x in [1 .. listlen(code)] {
        l = code[x];
        if (!match_regexp(l, regexp))
            continue;
        lr = strsed(l, regexp, replace, "g");
        .tell([((((("Change " + obj) + ".") + method) + "() line ") + x) + " from:", "  " + l, "to:", "  " + lr]);
        what = .prompt("? (yes, no, abort, abort-all) [yes] ");
        if ((!what) || (what in ["yes", "y"])) {
            code = replace(code, x, lr);
        } else if (what == "abort") {
            .tell("Aborting method ..");
            return;
        } else if (what == "abort-all") {
            throw(~stop, "Aborting grep replace");
        } else if (!(what in ["no", "n"])) {
            .tell(("Unknown command '" + what) + "', assuming 'no'.");
        }
        refresh();
    }
    if ((old_code != code) && (errs = obj.add_method(code, method)))
        .tell(((([((("Error in compilation of updated method " + obj) + ".") + method) + "():"] + (errs.prefix("  "))) + ["-- Method code: "]) + (code.prefix("  "))) + ["--"]);
};

protected method .help_list_cmd() {
    arg cmdstr, cmd, str;
    var node, out;
    
    (> .perms(caller(), 'command) <);
    if (!str)
        node = .current_node();
    else
        node = .parse_help_reference(str);
    if (!(node.is($help_node)))
        return (node.namef('ref)) + " is not a descendant of $help_node.";
    return (["@hwrite " + node] + ((node.body()).uncompile())) + ["."];
};

protected method .help_write_cmd() {
    arg cmdstr, cmd, str;
    var node, text, errors, ignore;
    
    (> .perms(caller(), 'command) <);
    if (!str) {
        node = .current_node();
    } else {
        catch any {
            node = .parse_help_reference(str);
        } with {
            .tell(("-- " + ((traceback()[1])[2])) + " --");
            .read("-- Ignoring until '.' or @abort --");
            return "Done ignoring.";
        }
    }
    if (!(node.is($help_node))) {
        ignore++;
        .tell((node.namef('ref)) + " is not a descendant of $help_node, ignoring.");
    }
    if (!(node.is_writable_by(this()))) {
        ignore++;
        .tell(("You cannot write help on " + (node.name())) + ", ignoring.");
    }
    text = .read(("-- Enter CML text for " + (node.namef('ref))) + " --");
    if (text == 'aborted)
        return;
    if (ignore)
        return "Done ignoring.";
    node.set_body(text);
    return ("New help text set for " + (node.namef('ref))) + ".";
};

protected method .id_cmd() {
    arg cmdstr, cmd, obj;
    
    (> .perms(caller(), 'command) <);
    obj = .match_env_nice(obj);
    if (type(obj) == 'frob)
        return ["The target object was a frob."];
    .tell((((((((obj.namef('xref)) + " ") + ($object_lib.see_perms(obj))) + " ") + toliteral(obj.parents())) + " ") + tostr(obj.size())) + " bytes");
};

protected method .join_cmd() {
    arg cmdstr, cmd, who;
    var loc, p, user;
    
    (> .perms(caller(), 'command) <);
    if (!who) {
        .tell("Specify a user to join.");
        return;
    }
    catch any {
        if ((who[1]) in "$#") {
            user = (> $object_lib.to_dbref(who) <);
            if (!(user.has_ancestor($thing)))
                return "You can only join things in the VR.";
        } else {
            user = (> $user_db.search(who) <);
        }
    } with {
        .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())) + ".");
};

protected method .list_cmd() {
    arg cmdstr, cmd, args;
    var i, pattern, ref, methods, s, def, method, opts, str, m, d, out, type;
    
    (> .perms(caller(), 'command) <);
    if ((opts = .get_setting("@list-options", $programmer))) {
        opts = $parse_lib.opt(opts, "n?umbers", "t?extdump");
        opts = union(args[3], opts[2]);
    } else {
        opts = args[3];
    }
    if ((i = (| "n?umbers" in (opts.slice(1)) |)) && ((opts[i])[3]))
        type = 'numbered;
    else if ((i = (| "t?extdump" in (opts.slice(1)) |)) && ((opts[i])[3]))
        type = 'textdump;
    else
        type = 'normal;
    ref = args[1];
    if ((ref[1]) == 'variable)
        return ((("The reference " + (ref[3])) + ",") + ((ref[4]) || "")) + " is not for a method.";
    if ((ref[1]) == 'object)
        return ("The reference " + (ref[3])) + " is not for a method.";
    def = (| (ref[2]).find_method(tosym(ref[4])) |);
    if (def) {
        pattern = ref[4];
        methods = [tosym(ref[4])];
    } else {
        if (ref[4])
            pattern = ref[4];
        else
            pattern = .get_setting("match-default", $programmer);
        def = ref[3];
        m = .get_setting("match-with", $programmer);
        methods = [];
        for method in (def.methods()) {
            if (tostr(method).(m)(pattern))
                methods += [method];
        }
        if (!methods)
            return .tell((("No method found matching " + def) + ".") + pattern);
    }
    cmd = .get_setting("@program-options", $programmer);
    out = [];
    for method in (methods)
        out += .format_method(def, method, type, cmd);
    return out;
};

protected method .local_edit_cmd() {
    arg cmdstr, cmd, args;
    var ref, edited, code, def, meth, i;
    
    (> .perms(caller(), 'command) <);
    ref = args[1];
    if ((ref[1]) == 'variable)
        return ((("The reference " + (ref[3])) + ",") + ((ref[4]) || "")) + " is not for a method.";
    if ((ref[1]) == 'object)
        return ("The reference " + (ref[3])) + " is not for a method.";
    if ((ref[3]) && (!((ref[3]).is_writable_by(this()))))
        return "You cannot program on that object.";
    if ((!(ref[4])) || (!((ref[4]).valid_ident())))
        return ("The method name '" + (ref[4])) + "' is not acceptable.";
    meth = tosym(ref[4]);
    catch ~methodnf {
        def = (ref[3]).find_method(meth);
        if (!(def.is_writable_by(this())))
            return ("You cannot program on " + def) + ".";
    } with {
        return ((("Method " + (ref[3])) + ".") + meth) + "() not found.";
    }
    if ((i = "e?dited" in ((args[3]).slice(1)))) {
        if (!(((args[3])[i])[3])) {
            if (!($sys.is_admin(this())))
                return "Only admins can shut off edited comments.";
        } else {
            edited = 1;
        }
    } else {
        edited = 1;
    }
    if (edited) {
        edited = (("// $#Edited: " + ($time.format("%d %h %y %H:%M"))) + " ") + this();
        if (i && (((args[3])[i])[4]))
            edited += ": " + (((args[3])[i])[4]);
    }
    catch ~perm
        code = def.list_method(meth);
    with
        return (traceback()[1])[2];
    return ([(((((("#$# edit name: " + def) + ".") + meth) + " upload: @program ") + def) + ".") + meth] + (code.prefix("    "))) + ["."];
};

protected method .managed_cmd() {
    arg cmdstr, cmd, args;
    var manager, managed, obj, out, len;
    
    (> .perms(caller(), 'command) <);
    manager = (| .match_environment(args) |);
    if (!manager) {
        manager = (| $user_db.search(args) |);
        if (!manager)
            return ("Unable to find \"" + args) + "\".";
    }
    managed = manager.managed();
    if (!managed)
        return (manager.namef('ref)) + " does not manage any objects.";
    out = [(manager.namef('ref)) + " manages:"];
    len = (.linelen()) / 2;
    for obj in (managed) {
        if (!valid(obj)) {
            .tell(("  ** invalid object (" + obj) + ") **");
            continue;
        }
        out += [(("  " + ((obj.namef('xref)).pad(len))) + " ") + ($object_lib.see_perms(obj, ["", ""]))];
    }
    return out + ["---"];
};

protected method .move_cmd() {
    arg cmdstr, cmd, args;
    var src, dest, comment, i, how;
    
    (> .perms(caller(), 'command) <);
    
    // is this actually @copy|@cp?
    how = match_begin(cmd, "@c") ? 'copy : 'move;
    
    // drop back to $builder.move_cmd if it is just an object
    if (((args[1])[1]) == 'object) {
        if (how == 'copy)
            return "You cannot copy objects!";
        return (> pass(cmdstr, cmd, [(args[1])[2], args[2], args[3]]) <);
    }
    
    // options
    if ((i = "c?omment" in ((args[3]).slice(1))))
        comment = (((args[3])[i])[4]) || (((args[3])[i])[3]);
    else
        comment = 1;
    
    // move|copy a method or var
    src = args[1];
    args = args[2];
    if ((args[1]) == "to")
        args = delete(args, 1);
    if (!args)
        return ((("You have to " + how) + " ") + (what.namef('ref))) + " somewhere.";
    catch ~objnf
        dest = (> $parse_lib.ref(args.join()) <);
    with
        return (traceback()[1])[2];
    if ((dest[1]) == 'object)
        dest = [src[1], dest[2], dest[3], src[4], 0];
    if ((src[1]) != (dest[1]))
        return ((((("You cannot " + how) + " a ") + (src[1])) + " to a ") + (dest[1])) + ".";
    if (!(src[4]))
        return ("Invalid " + (src[1])) + " reference, no name specified.";
    if (!(dest[4]))
        dest = replace(dest, 4, src[4]);
    if (((src[3]) == (dest[3])) && ((src[4]) == (dest[4])))
        return ((("Why do you want to " + how) + " the ") + (src[1])) + " to itself?";
    catch ~symbol {
        src = replace(src, 4, (> tosym(src[4]) <));
        dest = replace(dest, 4, (> tosym(dest[4]) <));
    } with {
        return ("You cannot specify wildcards in the " + (src[1])) + " name.";
    }
    if ((how == 'move) && (!((src[3]).is_writable_by(this()))))
        return ("You do not have permission to move from " + (src[3])) + ".";
    if (!((dest[3]).is_writable_by(this())))
        return ((("You do not have permission to " + how) + " to ") + (dest[3])) + ".";
    catch any
        (> .(tosym("_move_" + (src[1])))(how == 'move, src[3], src[4], dest[3], dest[4], comment) <);
    with
        return (traceback()[1])[2];
    return ((((("You " + how) + " ") + ($parse_lib.buildref(@src))) + " to ") + ($parse_lib.buildref(@dest))) + ".";
};

protected method .new_editor_session() {
    arg ref, opts, type;
    var def, code, name;
    
    switch (ref[1]) {
        case 'variable:
            (> .tell_error("", "Variable editor not yet implemented.") <);
        case 'method:
            def = (| (ref[2]).find_method(tosym(ref[4])) |);
            if (!def) {
                def = ref[3];
                code = [];
            } else {
                code = def.list_method(tosym(ref[4]));
            }
            (> .invoke_editor(this(), '_edit_method_callback, code, [def, tosym(ref[4])]) <);
        default:
            return (> pass(ref, opts, type) <);
    }
    if (.active_editor())
        return [("Editing " + ((.active_editor()).session_name())) + ".", "Type 'help' to list available commands."];
    else
        return ["Remote editing invoked."];
};

protected method .new_help_node_cmd() {
    arg cmdstr, cmd, args;
    var new, name, p, parent, i, syn, m, index, opts, objname, o;
    
    (> .perms(caller(), 'command) <);
    syn = ("=> Syntax: `" + cmd) + " [<parent node>] [options]`";
    [args, opts] = args;
    o = opts.slice(1);
    if ((i = "n?amed" in o)) {
        name = (opts[i])[4];
        if (!name)
            return [syn, "Option +n?amed requires a followup argument."];
    }
    if ((i = "o?bjname" in o)) {
        objname = (| ((opts[i])[4]).to_symbol() |);
        if (!objname)
            return [syn, "Option +o?bjname requires a followup argument."];
    }
    
    // now use 'i' as the string rep of 'index'
    if ((i = "i?ndex" in o)) {
        i = (opts[i])[4];
        if (!i)
            return [syn, "Option +i?ndex requires a followup argument."];
    }
    
    // now figure out the parent node
    if (args) {
        p = args[1];
        catch any
            parent = (> .parse_help_reference(p) <);
        with
            return (traceback()[1])[2];
    } else {
        parent = .current_node();
    }
    
    // figure out the index
    if (i) {
        if ((i[1]) in ["$", "#"])
            index = (| $object_lib.to_dbref(i) |);
        else
            index = $help_index.match_children(i);
        if (!index)
            return [syn, ("!  Unable to find index '" + i) + "'"];
        if (!(index.has_ancestor($help_index)))
            return [syn, ("!  '" + (index.namef('ref))) + "' is not a help index."];
    }
    
    // create it
    new = (> parent.spawn() <);
    .tell(((("Created new node " + new) + " from ") + (parent.namef('ref))) + ".");
    
    // change its objname?
    if (objname) {
        catch any {
            (> new.set_objname(objname) <);
            .tell(("Changed node's objname to " + new) + ".");
        } with {
            .tell("set_objname(): " + ((traceback()[1])[2]));
        }
    }
    
    // set its name?
    if (name) {
        catch any {
            (> new.set_name(name) <);
            .tell(("Changed node's name to " + (new.name())) + ".");
        } with {
            .tell("set_name(): " + ((traceback()[1])[2]));
        }
    } else {
        .tell(("No name specified, set it with `@rename " + new) + " to <name>`");
    }
    
    // set its index?
    if (index) {
        catch any {
            (> new.set_index(index) <);
            .tell(("Set node's index to " + (index.namef('ref))) + ".");
        } with {
            .tell("set_index(): " + ((traceback()[1])[2]));
        }
    } else {
        .tell(("No index specified, set it with `@set " + new) + ":index=<index>`");
    }
};

public method .parse_match_with() {
    arg value, @args;
    
    if (value in ["regexp", "pattern", "begin"])
        return tosym("match_" + (value.lowercase()));
    throw(~perm, "You can match with: regexp, pattern, begin.");
};

public method .parse_methodcmd_options() {
    arg syntax, args, @more;
    var o, opt, opts, out, r, l;
    
    o = ([@more, []][1]) + [["pub?lic"], ["r?oot"], ["dr?iver"], ["pri?vate"], ["pro?tected"], ["no?override"], ["s?yncronized"], ["l?ocked"], ["na?tive"]];
    opts = #[['exists, 0], ['ignore, 0], ['mflags, []], ['mstate, 'public], ['error, 0]].union([@more, #[], #[]][2]);
    args = $parse_lib.getopt(args, o);
    if (!(args[1])) {
        out = [];
        for opt in (o)
            out += ["  +|-" + (opt[1])];
        (> .tell_error(syntax, ["Valid options:"] + (out.lcolumnize())) <);
    }
    r = (| $parse_lib.ref((args[1]).join()) |);
    if (!r) {
        opts = opts.add('error, "Invalid <object>.<method> reference.");
        opts = opts.add('ignore, 1);
    }
    if (!((r[4]).valid_ident())) {
        opts = opts.add('error, (((r[2]) + ".") + tostr(r[4])) + "() is not a valid method reference.");
        opts = opts.add('ignore, 1);
    }
    r = replace(r, 4, tosym(r[4]));
    if ((r[2]) && (!((r[2]).is_writable_by(this())))) {
        opts = opts.add('error, ("You cannot program " + (r[2])) + ".");
        opts = opts.add('ignore, 1);
    }
    if ((| (r[2]).find_method(r[4]) |) == (r[2])) {
        opts = opts.add('mflags, (r[2]).method_flags(r[4]));
        opts = opts.add('mstate, (r[2]).method_access(r[4]));
        opts = opts.add('exists, 1);
    }
    opts = opts.add('object, r[2]);
    opts = opts.add('method, r[4]);
    for opt in (args[2]) {
        switch (opt[1]) {
            case "pub?lic", "r?oot", "dr?iver", "pri?vate", "pro?tected":
                opts = opts.add('mstate, (opt[1]).to_symbol());
            case "no?override", "s?yncronized":
                opts = opts.add('mflags, (opts['mflags]).setadd((opt[1]).to_symbol()));
            case "l?ocked":
                .tell("You cannot set the locked flag on a method.");
            case "n?ative":
                .tell("You cannot set the native flag on a method.");
            default:
                if (!(opt[1])) {
                    .tell(("Unknown option: \"" + (opt[2])) + "\"");
                    .tell("Valid options: " + ((o.slice(1)).to_english()));
                    continue;
                }
                opts = opts.add((opt[1]).to_symbol(), [opt[3], opt[4]]);
        }
    }
    return opts;
};

protected method .program_cmd() {
    arg cmdstr, com, args, @more;
    var ref, o, i, ops, ign, ed, fl, meth, ex, acc, warn, errs, code, line, errs, code;
    
    (> .perms(caller(), 'command) <);
    ops = args[3];
    ref = args[1];
    
    // verify what we have is correct
    if (!(meth = (| tosym(ref[4]) |))) {
        ign++;
        .tell(("The method name '" + (((ref[4]) == 0) ? "" : (ref[4]))) + "' is not acceptable.");
    }
    if ((!ign) && ((ref[3]) && (!((ref[3]).is_writable_by(this()))))) {
        ign++;
        .tell(("You cannot program on " + ((ref[3]).namef('ref))) + ".");
    }
    if ((!ign) && ((| (ref[3]).find_method(meth) |) == (ref[3])))
        ex++;
    
    // ok, go on with options
    o = ops.slice(1);
    if ((i = "e?dited" in o)) {
        if (!((ops[i])[3])) {
            if (!($sys.is_admin(this()))) {
                ign++;
                .tell("Only admins can shut off edited comments.");
            }
        } else {
            ed = 1;
        }
    } else {
        ed = 1;
    }
    if (ed) {
        ed = (("// $#Edited: " + ($time.format("%d %h %y %H:%M"))) + " ") + this();
        if (i && ((ops[i])[4]))
            ed += ": " + ((ops[i])[4]);
    }
    if ((i = "f?lags" in o))
        fl = $parse_lib.parse_method_flags((ops[i])[4]);
    else if (ex)
        fl = (ref[3]).method_flags(meth);
    else
        fl = [];
    if ((i = "a?ccess" in o))
        acc = $parse_lib.parse_method_access((ops[i])[4]);
    else if (ex)
        acc = (ref[3]).method_access(meth);
    else
        acc = 'public;
    if ((i = "w?arnings" in o))
        warn = (ops[i])[4];
    else
        warn = 1;
    
    // now get on with it already
    if (ign)
        line = "Ignoring input until \".\" or \"@abort\"";
    else if (ex)
        line = ((((("Reprogramming " + acc) + " method ") + (ref[3])) + ".") + meth) + "()";
    else
        line = ((((("Programming " + acc) + " method ") + (ref[3])) + ".") + meth) + "()";
    if (fl)
        line += (" [" + (fl.to_english())) + "]";
    code = more ? (more.subrange(2)) : (.read(("-- " + line) + " --"));
    if (type(code) == 'symbol) {
        switch (code) {
            case 'aborted:
                return;
            case 'engaged:
                return "Sorry, you are already reading on this connection.";
            default:
                return "Unknown response from the read parser: " + code;
        }
    }
    if (ign)
        return "Finished ignoring input.";
    if (ed)
        code += [ed];
    catch any {
        if ((errs = (ref[3]).add_method(code, meth)))
            return errs;
        (> (ref[3]).set_method_flags(meth, fl) <);
        (> (ref[3]).set_method_access(meth, acc) <);
        if ((line = (> $code_lib.verify_code(code, meth, warn) <)))
            .tell(line);
        return ((((("Method " + (ref[3])) + ".") + meth) + "() ") + (ex ? "re" : "")) + "compiled";
    } with {
        return (traceback()[1])[2];
    }
};

protected method .rehash_cmd() {
    arg cmdstr, cmd;
    var c, o, p;
    
    (> .perms(caller(), 'command) <);
    .tell("Rehashing your commands...");
    .purge_caches();
    for p in (parents())
        p.cache_uninit();
    for cmd in (.local_commands()) {
        for c in (cmd[2])
            .add_to_local_cache(c[1]);
    }
    for p in (parents())
        p.cache_init();
    .tell("Done.");
};

protected method .show_cmd() {
    arg cmdstr, com, args;
    var show, match, i, chop, f, obj, out;
    
    (> .perms(caller(), 'command) <);
    if (((args[1])[1]) == 'object)
        show = ['method, 'variable];
    else if ((args[1])[5])
        show = [(args[1])[1], (args[1])[5]];
    else
        show = [(args[1])[1]];
    if ((i = "c?hop" in ((args[3]).slice(1))))
        chop = ((args[3])[i])[3];
    else
        chop = 1;
    if ((args[1])[4])
        f = (args[1])[4];
    else
        f = .get_setting("match-default", $programmer);
    match = .get_setting("match-with", $programmer);
    obj = (args[1])[3];
    if (type(obj) == 'frob)
        return ["The target object was a frob.  Please use @exam instead."];
    .tell([((("Object:  " + obj) + " [") + ((obj.size()).to_english())) + " bytes]", "Parents: " + ((obj.parents()).join(", "))]);
    if ('method in show) {
        if (!(obj.has_flag('methods, this())))
            .tell("  ** No permission to list methods **");
        else
            .tell(._show_methods(obj, f, match, chop));
    }
    if ('variable in show) {
        if (!(obj.has_flag('variables, this())))
            .tell("  ** No permission to show variables **");
        else
            .tell(._show_variables(obj, f, match, chop));
    }
    .tell("---");
};

public method .spawn_cmd() {
    arg cmdstr, cmd, args;
    var match, name, parents, p, line, set, nprog, new, t;
    
    (> .perms(caller(), 'command) <);
    if ((match = match_template(args, "* named *"))) {
        name = match[3];
        args = explode(match[1]);
    } else {
        args = args.explode_quoted();
        if (!args)
            return "Spawn from nothing?";
        name = "";
    }
    
    // programmers get additional privs
    nprog = !(.is($programmer));
    parents = [];
    for p in (args) {
        catch any {
            p = (> .match_env_nice(p) <);
        } with {
            .tell((traceback()[1])[2]);
            continue;
        }
        if ((!(p.is($thing))) && nprog) {
            .tell((p.namef('ref)) + " is not a VR object, you may only spawn VR objects.");
            continue;
        }
        if (!(p.has_flag('fertile))) {
            .tell((p.namef('ref)) + " is not a fertile object.");
            continue;
        }
        parents += [p];
    }
    parents = parents.compress();
    if (!parents)
        return "No capable parents.";
    if (name) {
        catch any
            name = (> $code_lib.parse_name(name) <);
        with
            return (traceback()[1])[2];
    }
    
    // spawn from the first parent, add the others
    catch any {
        new = (> (parents[1]).spawn() <);
        (> new.chparents(@parents) <);
    
        // let the user know whats up
        .tell(((("Spawned new object " + (new.namef('ref))) + " from ") + (map p in (parents) to (p.namef('ref)).to_english())) + ".");
        if (new.is($located))
            (> new.move_to(this()) <);
        if (name && ((((name[1])[1])[1]) == "$")) {
            name = (> tosym(((name[1])[1]).subrange(2)) <);
            (> new.set_objname(name) <);
            return ("Object name changed to: " + new) + ".";
        } else if (name) {
            if (!(new.has_ancestor($has_name)))
                return new + " cannot be given a VR name.";
            (> new.set_name(@name[1]) <);
            for t in (name[2])
                (> new.add_name_template(t) <);
            return (((("Renamed " + new) + " to \"") + (new.name())) + "\"") + ((new.name_templates()) ? ((" (" + ((new.name_templates()).to_english())) + ")") : "");
        }
    } with {
        .tell((traceback()[1])[2]);
        if (valid(new)) {
            line = new.namef('xref);
            new.destroy();
            if (valid(new))
                return ("Unable to destroy new object " + line) + ".";
            else
                return ("Sucessfully destroyed new object " + line) + ".";
        }
    }
};

protected method .trace_method_cmd() {
    arg cmdstr, cmd, ref;
    var method, current, trace, syn, minfo, line, anc, len, out, m;
    
    (> .perms(caller(), 'command) <);
    if ((ref[1]) != 'method)
        return toliteral(cmd) + " requires a full method reference.";
    catch any {
        method = (> tosym(ref[4]) <);
        current = (> (ref[2]).find_method(method) <);
        trace = [];
        while (current) {
            trace += [current];
            current = (| (ref[2]).find_next_method(method, current) |);
        }
    } with {
        if (error() == ~symbol)
            return ("Invalid method name \"" + (ref[4])) + "\".";
        return (traceback()[1])[2];
    }
    .tell(((("Method trace of " + (ref[2])) + ".") + (ref[4])) + "():");
    len = .linelen();
    out = [];
    for anc in (trace.reverse()) {
        m = anc.method_info(method);
        out += [strfmt("%5l %4r %l.%l(%l)", $object_lib.parse_method_flags(m[6]), m[4], anc, method, m[1])];
    }
    return out;
};

public method .undefine_setting_cmd() {
    arg cmdstr, cmd, args;
    var name, definer, syn, name, m;
    
    syn = ("Syntax: `" + cmd) + " <definer>:<setting>`";
    if (!(m = regexp(args, "^ *([^:]+):([^ $]+)")))
        return syn;
    [definer, name] = m;
    catch any
        definer = (> .match_environment(definer) <);
    with
        return (traceback()[1])[2];
    if (!name)
        return syn;
    catch any
        (> definer.undefine_setting(name) <);
    with
        return (traceback()[1])[2];
    return ((("Undefined setting " + definer) + ":") + name) + ".";
};

protected method .which_cmd() {
    arg cmdstr, command, str;
    var m, c, l, t, p, s, cmds, def, out, dname, line, lcache, rcache;
    
    (> .perms(caller(), 'command) <);
    if (!str)
        return ("Syntax: `" + command) + " <partial or full template>`";
    m = #[];
    t = (str.explode())[1];
    for p in (.ancestors()) {
        if (p == $has_commands)
            break;
        cmds = ._which_cmd(str, p, 'local_commands, " ");
        cmds += ._which_cmd(str, p, 'remote_commands, "*");
        if (cmds)
            m = m.add(p, cmds);
    }
    if (!m)
        return ("No commands found matching the template \"" + str) + "\".";
    l = (.linelen()) / 2;
    out = [("Commands matching the template \"" + str) + "\":"];
    lcache = .local_cache();
    rcache = .remote_cache();
    for def in (m) {
        dname = (" " + (def[1])) + ".";
        for c in (def[2]) {
            line = ((((c[1]) + ((c[2]).pad(l))) + dname) + tostr(c[3])) + "()";
            s = (((((c[2]).explode())[1]).strip("?")).explode("|"))[1];
            if ((| lcache[s] |) || (| rcache[s] |))
                line = " " + line;
            else
                line = "!" + line;
            out += [line];
        }
    }
    return out;
};