/
ColdCore-2.1/
name root 1
name foundation 2
name core 3
name event_handler 4
name has_commands 5
name environment 6
name has_messages 7
name public 8
name network 9
name help_root 10
name text 11
name messaged 12
name has_settings 13
name named 14
name gender 15
name libraries 16
name misc 17
name utilities 18
name frob 19
name sys 0
name control 20
name backdoor 21
name described 22
name gendered 23
name physical 24
name command_cache 25
name exit 26
name located 27
name thing 28
name location 29
name located_location 30
name body 31
name user_interfaces 32
name interaction 33
name mail_root 34
name mail_list 35
name mail_ui 36
name command_aliases 37
name bad_commands 38
name user_data 39
name help_ui 40
name messages_ui 41
name settings_ui 42
name user 43
name place 44
name connection 45
name daemon 46
name connection_interface 47
name help_index 48
name help 49
name help_node 50
name note 51
name project 52
name mail_message 53
name event_frob 54
name gender_female 55
name gender_male 56
name gender_plural 57
name gender_neuter 58
name dictionary 59
name time_root 60
name buffer 61
name string 62
name command_lib 63
name http_lib 64
name object_lib 65
name integer 66
name parse_lib 67
name code_lib 68
name list 69
name security 70
name help_lib 71
name ctext_lib 72
name mail_lib 73
name time_lib 74
name data_lib 75
name symbol 76
name tree 77
name db 78
name user_parsers 79
name trie 80
name filters 81
name lock_parser 82
name cml2 83
name world 84
name root_evaluator 85
name has_hooks 86
name lag_watcher 87
name channels 88
name places 89
name motd 90
name heart 91
name projects 92
name scheduler 93
name housekeeper 94
name logic_frob 95
name realms_frob 96
name ctext_frob 97
name ctext_tag 98
name thing_frob 99
name read_parser 100
name ctext2_frob 101
name message_frob 102
name editors 103
name builder 104
name guest 105
name reaper 106
name no_one 107
name player 108
name nothing 109
name nowhere 110
name body_cave 111
name void 112
name detailed_place 113
name the_pit 114
name login_connection 115
name http_connection 116
name http_daemon 117
name login_daemon 118
name login_interface 119
name http_interface 120
name help_index_root 121
name help_summary 122
name log 123
name movement_event 124
name time 125
name dark_time 126
name mail_envelope 127
name mail_address 128
name registry 129
name mail_db 130
name null_parser 131
name command_aliases_parser 132
name command_parser 133
name conference_parser 134
name epic_filter 135
name wrap_filter 136
name ctext_filter 137
name cml2_evaluator 138
name cml2_form 139
name cml2_compiler 140
name xor 141
name and 142
name lock_frob 143
name not 144
name or 145
name true 146
name false 147
name realm_of_creation 148
name ctext_generator 149
name ctext_format 150
name wearable_frob 151
name help_editing_ui 152
name tree_ui 153
name editor 156
name set_ui 157
name generic_editor 158
name programmer 159
name storyteller 161
name on_location 162
name in_location 163
name http_log 171
name reaper_log 172
name login_log 173
name mail_list_news 174
name mail_list_bugs 175
name place_db 176
name user_db 177
name cml2_format 178
name cml2_base_eval 179
name cml2_uncompiler 180
name or_lock_frob 181
name true_lock_frob 182
name false_lock_frob 183
name not_lock_frob 184
name object_lock_frob 185
name and_lock_frob 186
name inside_lock_frob 187
name admin 188
name code_editor 190
name note_editor 191
name lost_and_found 192
name cml2_telnet_format 193
name cml2_html_format 194


object $root

var $root manager $root
var $root owners [$root]
var $root trusted []
var $root owned 0
var $root child_index 12
var $root fertile 1
var $root inited 0
var $root quota 75000
var $root managed 0
var $root writers [$root]
var $root objname 'root
var $root created_on 0
var $root indestructable 1
var $root flags ['parameters, 'methods, 'code, 'fertile, 'core]
var $root quota_exempt 0
var $root writes 0
var $root trusted_by 0

root init_root
    .change_manager(this());
    flags = ['parameters, 'methods, 'code];
    created_on = time();
    .add_owner(this());
.

root uninit_root
    var obj;
    
    (| manager.del_managed_obj() |);
    catch any {
        for obj in (.managed())
            obj.change_manager($reaper);
    }
    catch any {
        for obj in (.writers('literal))
            .del_writer(obj);
    }
    catch any {
        for obj in (.writes())
            obj.del_writer(this());
    }
    catch any {
        for obj in (.trusted('literal))
            .del_trusted(obj);
    }
    catch any {
        for obj in (.trusted_by())
            obj.del_trusted(this());
    }
    catch any {
        for obj in (.owners())
            .del_owner(obj, 1);
    }
    catch any {
        for obj in (.owned())
            obj.del_owner(this());
    }
    
    // tell $sys we are going away
    $sys.sender_going_away();
.

public initialize: disallow_overrides
    var ancestors, ancestor, pos, len;
    
    if (caller() != $sys && sender() != this())
        throw(~perm, "Caller is not $sys and sender is not this.");
    if (inited)
        throw(~perm, "Already initialized.");
    ancestors = ancestors();
    len = ancestors.length();
    for pos in [0 .. len - 1] {
        (> ._initialize_ancestor(ancestors[len - pos]) <);
    
        // pause();
    }
    inited = 1;
.

public uninitialize: disallow_overrides
    var ancestor, errors;
    
    (> .perms(caller(), $root, $sys) <);
    errors = [];
    for ancestor in (ancestors()) {
        catch any {
            ._uninitialize_ancestor(ancestor);
        } with handler {
            errors = [@errors, traceback()];
        }
    
        // pause();
    }
    return errors;
.

public perms: disallow_overrides
    arg what, [args];
    var flag, second;
    
    if (!args)
        args = ['writer];
    if (type(args[1]) == 'symbol) {
        switch (args[1]) {
            case 'this:
                if (what != this() && !$sys.is_system(what))
                    throw(~perm, "Permission Denied: " + what.objname() + " is not this.", what);
            case 'system:
                if (!$sys.is_system(what))
                    throw(~perm, "Permission Denied: " + what.objname() + " is not of the system.", what);
            case 'manager:
                if (.manager() != what && !$sys.is_system(what))
                    throw(~perm, "Permission Denied: " + what.objname() + " is not the manager.", what);
            case 'trusts:
                if (!.trusts(what))
                    throw(~perm, "Permission Denied: " + what.objname() + " is not a trustee.", what);
            default:
                if (!.is_writable_by(what))
                    throw(~perm, "Permission Denied: " + what.objname() + " is not a writer.", what);
        }
    } else if (type(what) == 'dbref) {
        if (!(what in args))
            throw(~perm, what.objname() + " is not one of: " + $list.to_english($list.map(args, 'namef, 'ref)), what);
    }
.

root _change_parents: disallow_overrides
    arg parents;
    var old, old_p, init, uninit, ancestor, pos, errors;
    
    if (!parents)
        throw(~noparents, "Objects must have at least 1 parent");
    
    // Perform the actual change.
    old = ancestors();
    old_p = .parents();
    (> $sys.change_sender_parents(parents) <);
    
    // Figure out new ancestors to initialize, and old ones to uninitialize.
    init = [];
    uninit = old;
    for ancestor in (ancestors()) {
        uninit = uninit.setremove(ancestor);
        if (!(ancestor in old))
            init = [@init, ancestor];
    }
    
    // Initialize the new ancestors.
    catch any {
        for ancestor in (init)
            ._initialize_ancestor(ancestor);
    } with handler {
        // Initialization error
        // deinitialize parents we initialized, and fall back to old parents.
        pos = ancestor in init;
        for ancestor in (init.subrange(1, pos))
            (| ._uninitialize_ancestor(ancestor) |);
        (> $sys.change_sender_parents(old_p) <);
        throw(~init, "Failed to initialize new ancestors.", traceback());
    }
    
    // Uninitialize the old ancestors.
    for ancestor in (uninit)
        (| .(tosym("uninit_" + tostr(ancestor.objname('symbol))))() |);
.

public chparents
    arg [parents];
    var parent;
    
    if (!(| .perms(sender(), 'manager) |))
        (> .perms(caller(), $root, $sys) <);
    if (!parents)
        throw(~noparents, "There must be at least 1 parent for each object.");
    
    // Notify parents of impending change.
    for parent in (parents)
        (> parent.will_inherit(sender()) <);
    
    // Everything's okay, go ahead and try it.
    ._change_parents(parents);
.

public will_inherit
    arg obj;
    
    // Throw an error if it's not okay for obj to inherit from us.
    if (!.has_flag('fertile, sender()) && !.trusts(obj) && !obj.has_ancestor(this()))
        throw(~perm, .objname() + " refuses to be parent of " + obj.objname() + ".");
.

public manager: disallow_overrides
    return manager || $control;
.

public owners: disallow_overrides
    return owners || [$control];
.

public writers: disallow_overrides
    arg [literal];
    
    if (literal)
        return writers || [];
    
    // for speed, just add rather than setadd, use .compress() later if necessary
    return (writers || []) + [.manager(), this()];
.

public trusted: disallow_overrides
    arg [literal];
    
    if (literal)
        return trusted || [];
    return (trusted || []) + .writers();
.

public is_writable_by: disallow_overrides
    arg obj;
    
    return (| obj in .writers() |) || $sys.is_system(obj);
.

public trusts: disallow_overrides
    arg obj;
    
    return (| obj in .trusted() |) || obj == $scheduler || $sys.is_system(obj);
.

root set_manager: disallow_overrides
    arg new;
    
    if (type(new) != 'dbref)
        throw(~invarg, "Managers must be given as a single dbref.");
    if (new == $control)
        (| clear_var('manager) |);
    else
        manager = new;
.

public change_manager: disallow_overrides
    arg new;
    var old;
    
    caller() == definer() || (> .perms(sender(), 'manager) <);
    if (type(new) != 'dbref)
        throw(~invarg, "Managers must be given as a single dbref.");
    old = .manager();
    .set_manager(new);
    old.del_managed_obj();
    new.add_managed_obj();
.

public add_writer: disallow_overrides
    arg obj;
    
    (> .perms(sender(), 'manager) <);
    writers = (writers || []).setadd(obj);
    obj.add_writable_obj();
.

public del_writer: disallow_overrides
    arg obj;
    
    caller() == definer() || (> .perms(sender(), 'manager) <);
    writers = (writers || []).setremove(obj);
    if (!writers)
        (| clear_var('writers) |);
    obj.del_writable_obj();
.

public add_trusted: disallow_overrides
    arg obj;
    
    caller() == definer() || (> .perms(sender(), 'manager) <);
    trusted = (trusted || []).setadd(obj);
    obj.add_trusted_obj();
.

public del_trusted: disallow_overrides
    arg obj;
    
    (> .perms(sender(), 'manager) <);
    trusted = (trusted || []).setremove(obj);
    if (!trusted)
        clear_var('trusted);
    obj.del_trusted_obj();
.

public as_this_run
    arg obj, method, args;
    
    if (!.trusts(sender()))
        throw(~perm, "Sender not allowed to gain access to object perms.");
    return (> obj.(method)(@args) <);
.

public add_parent
    arg parent;
    
    (> .perms(sender(), 'manager) <);
    if (parent in .parents())
        throw(~parent, .objname() + " already has " + parent.objname() + " as a parent.");
    if (!valid(parent))
        throw(~type, "Not a valid parent, must send a dbref or pointer");
    .chparents(@.parents(), parent);
.

public del_parent
    arg parent;
    var parents;
    
    (> .perms(sender(), 'manager) <);
    if (!valid(parent))
        throw(~type, "Not a valid parent, must send a valid object.");
    parents = .parents();
    if (!(parent in parents))
        throw(~parentnf, parent.objname() + " is not a parent of " + .objname() + ".");
    parents = parents.setremove(parent);
    (> .chparents(@parents) <);
.

public spawn: disallow_overrides
    arg [suffix];
    var obj, tmp, objname, owner, mngr, na;
    
    na = "!@#$%^&*()+-=~`'{}[]|/?\"\\,.<>;: ";
    if (!.has_flag('fertile, sender()))
        throw(~perm, "Not fertile or readable.");
    
    // available quota?
    if (!sender().quota_valid())
        throw(~quota, "Sender does not have the available quota");
    
    // Figure out the suffix from the arguments and child index.
    if (suffix) {
        suffix = suffix[1];
    
        // so they dont confuse child_index:
        if ($string.is_numeric(suffix))
            throw(~perm, "Can't specify a numeric suffix.");
    
        // so we get correct symbols & it is always lowercase:
        suffix = $string.strip(suffix, na);
    
        //.lowercase();
    } else {
        // make sure it doesn't exist already:
        objname = .objname('symbol).to_string();
        tmp = .objname('symbol);
        while ((| get_dbref(tmp) |)) {
            child_index = child_index + 1;
            tmp = tosym(objname + "_" + tostr(child_index));
        }
        suffix = tostr(child_index);
    }
    
    // Ask the system object for a child.
    obj = $sys.spawn_sender(suffix, sender());
    return obj;
.

public destroy: disallow_overrides
    // This doesn't actually destroy us immediately, but we will go away when
    // nothing is holding onto us any more.
    (> .perms(sender(), 'manager) <);
    if (.has_flag('core))
        throw(~perm, "This object is a core object, and cannot be destroyed!");
    (| .uninitialize() |);
    $sys.destroy_sender();
.

public add_parameter
    arg name, [args];
    var tmp, kid;
    
    // .add_parameter('name[, default-value, 'inherit])
    // Add the given parameter to this object.
    // if 'default-value' is included, initialize the variable to that value
    // if 'inherit is given the value is initialized on all descendents
    (> .perms(sender()) <);
    (> add_parameter(name) <);
    
    // The following code is a kludge as we can't send a default value to the
    // primitive.
    if (args) {
        tmp = tosym("__set_" + tostr(name) + "_" + tostr(random(1000)));
        catch any {
            compile([tostr(name) + " = " + toliteral(args[1]) + ";"], tmp);
            .(tmp)();
            if (args.length() > 1 && args[2] == 'inherit) {
                for kid in (.descendants())
                    kid.(tmp)();
            }
            del_method(tmp);
        } with handler {
            del_method(tmp);
            rethrow(error());
        }
    }
.

protected parameters
    return parameters();
.

public del_parameter: disallow_overrides
    arg name;
    var n, obj;
    
    (> .perms(sender()) <);
    
    // try and clear the variable on all of the descendants, before deleting
    // the parameter...
    (> .clear_variable(name) <);
    
    // now delete the parameter
    (> del_parameter(name) <);
.

public del_method
    arg name;
    
    (> .perms(sender()) <);
    (> del_method(name) <);
.

public methods: disallow_overrides
    if (!.has_flag('methods, sender()))
        throw(~perm, sender().objname() + " doesn't have permission to find methods on " + .objname());
    return methods();
.

public parents: disallow_overrides
    return parents();
.

public children: disallow_overrides
    return children();
.

public ancestors: disallow_overrides
    return ancestors();
.

public find_method: disallow_overrides
    arg name;
    
    if (!.has_flag('methods, sender()))
        throw(~perm, sender().objname() + " doesn't have permission to find methods on " + .objname());
    return (> find_method(name) <);
.

public find_next_method: disallow_overrides
    arg name, after;
    
    if (!.has_flag('methods, sender()))
        throw(~perm, sender().objname() + " doesn't have permission to find methods on " + .objname());
    return (> find_next_method(name, after) <);
.

public list_method
    arg [args];
    
    if (!.has_flag('code, sender()))
        throw(~perm, "Method code on " + .namef('ref) + " is not readable by " + sender().namef('ref));
    return (> list_method(@args) <);
.

public compile
    arg code, name;
    
    (> .perms(sender()) <);
    (| $sys.touch() |);
    return (> compile(code, name) <);
.

public show: disallow_overrides
    arg wildcard, chop, sinv, [what];
    var output, what, wildcard, actor, parent, readable, m, p, x, line;
    
    actor = sender();
    output = "Object: " + .namef('ref) + " [" + tostr(.size()) + " bytes]";
    output = [output, "Parents: " + $data_lib.unparse(parents())];
    if ('methods in what) {
        if (.has_flag('methods, sender()))
            output = output + ._show_methods(wildcard, actor);
        else
            output = output + ["  ** No permission to list Methods **"];
    }
    if ('parameters in what) {
        if (.has_flag('parameters, sender()))
            output = output + ._show_parameters(wildcard, actor, chop, sinv);
        else
            output = output + ["  ** No permission to list Parameters **"];
    }
    return output;
.

public has_ancestor: disallow_overrides
    arg obj;
    
    return has_ancestor(obj);
.

public eval: disallow_overrides
    arg code, [dest];
    var errors, result;
    
    dest = dest ? dest[1] | this();
    if (sender() != $scheduler)
        (> .perms(sender()) <);
    
    // Compile the code.
    errors = .compile(code, 'tmp_eval);
    if (errors)
        return ['errors, errors, 0, 0];
    
    // Evaluate the expression.  Be sure to remove it afterwards, so that no
    // one else can call it.
    catch any {
        result = (> dest.tmp_eval() <);
    } with handler {
        (| del_method('tmp_eval) |);
        rethrow(error());
    }
    (| del_method('tmp_eval) |);
    return ['result, result];
.

public method_info
    arg [args];
    var gen, i, definers, filter, method, public, private, protected, anc, ancs, root, driver, match;
    
    if (!.has_flag('methods, sender()))
        throw(~perm, "Method code on " + .namef('ref) + " is not readable by " + sender().namef('ref));
    
    // Just assume if they are sending arguments it is @display not @show
    if (args) {
        gen = args[1];
        definers = args[2];
        filter = args[3];
    } else {
        if (definers)
            gen = ['ancestor_to, definers[1]];
        else
            gen = ['generations, 1];
        filter = "";
    }
    public = private = protected = root = driver = [];
    ancs = .(gen[1])(gen[2]);
    if (definers)
        ancs = $list.intersection(ancs, definers);
    if (!ancs)
        ancs = [this()];
    match = (| .setting("match-with") |) || 'match_regexp;
    for anc in (ancs) {
        for method in (anc.methods()) {
            if ($string.(match)(filter, tostr(method))) {
                i = ._get_method_info(anc, method);
                switch (anc.method_state(method)) {
                    case 'root:
                        root = root + [i];
                    case 'driver:
                        driver = driver + [i];
                    case 'public:
                        public = public + [i];
                    case 'private:
                        private = private + [i];
                    case 'protected:
                        protected = protected + [i];
                }
            }
        }
    }
    return [public, protected, private, root, driver];
.

public data: disallow_overrides
    var par, data, out;
    
    if (!.has_flag('parameters, sender()))
        throw(~perm, sender().namef('ref) + " is not allowed to read parameters on " + .namef('ref));
    data = $sys.sender_data();
    out = #[];
    for par in (data) {
        // if the parent doesn't exist anymore, just let them see the data.
        if (!valid(par[1]) || par[1].has_flag('parameters, sender()))
            out = out.add(par[1], par[2]);
        else
            out = out.add(par[1], ["*** Permission Denied ***"]);
    }
    return out;
.

public size: disallow_overrides
    arg [args];
    
    args = [@args, 'int][1];
    switch (args) {
        case 'string:
            return tostr(size());
        case 'english:
            return $int.to_english(size());
        default:
            return size();
    }
.

public descendants: disallow_overrides
    var kids, i, c;
    
    kids = children();
    while ((| c = kids[i = i + 1] |))
        kids = kids.union(c.children());
    return kids;
.

public get_quota: disallow_overrides
    return quota;
.

public owned: disallow_overrides
    return owned || [];
.

public quota_valid: disallow_overrides
    if (quota_exempt == -1)
        return 1;
    if (!.is($user))
        return 0;
    return .quota_byte_usage() < .quota();
.

root del_owned_obj: disallow_overrides
    if (owned) {
        owned = owned.setremove(sender());
        (| clear_var('owned) |);
    }
.

root add_owned_obj: disallow_overrides
    owned = (owned || []).setadd(sender());
.

public match_children
    arg string;
    var children, child_names, c;
    
    children = .children();
    child_names = $list.map(children, 'namef);
    
    // direct matches first.
    for c in (child_names) {
        if (c == string)
            return children[c in child_names];
    }
    
    // ok, try partial matches
    for c in (child_names) {
        if ($string.match_begin(c, string))
            return children[c in child_names];
    }
    return 0;
.

public del_flag: disallow_overrides
    arg flag;
    
    (> .perms(sender(), 'manager) <);
    
    // let them add any flag they want
    flags = (flags || []).setremove(flag);
.

public _display_descendants
    arg space, checked, levels, maxlev;
    var space, checked, c, anc, biguglylist, id, perms;
    
    id = space + .namef('xref) + " " + $object_lib.see_perms(this());
    for anc in (checked) {
        if (.has_ancestor(anc))
            return [id + "  (above)"];
    }
    if (.parents().length() > 1)
        id = id + " (MI)";
    else
        id = id + "";
    biguglylist = [id];
    space = space + "   ";
    levels = levels + 1;
    
    // check children
    if (!maxlev || maxlev != levels) {
        for c in (.children()) {
            biguglylist = biguglylist + c._display_descendants(space, checked, levels, maxlev);
            checked = checked.setadd(c);
            pause();
        }
    }
    return biguglylist;
.

public _get_method_info: disallow_overrides
    arg anc, method;
    var code, lines, dis_flag, meth_args, flags, first_comment;
    
    code = anc.list_method(method);
    lines = code.length();
    if (lines > 5)
        code = code.subrange(1, 5);
    flags = anc.method_flags(method);
    if (code) {
        meth_args = "arg (.*);".sub(code[1]);
        if (meth_args) {
            meth_args = meth_args[1];
            code = code.delete(1);
        } else {
            meth_args = "";
        }
        if (code && (!code[1] || code[1][1] == "v"))
            code = code.delete(1);
        if (code && (!code[1] || code[1][1] == "v"))
            code = code.delete(1);
        first_comment = code ? code[1] + " " | " ";
        first_comment = first_comment[1] == "/" || first_comment[1] == "r" ? first_comment | "";
    } else {
        meth_args = "";
        first_comment = "";
    }
    return [anc, method, meth_args, flags, lines, first_comment];
.

public namef
    arg [args];
    var output, type, part, rval;
    
    if (!args)
        args = [['objname]];
    if (args[1] == 'ref || args[1] == 'xref)
        args = [['objname]];
    
    // the actual switch, punctuation is treated oddly (parens specifically)
    output = "";
    for part in (args) {
        type = type(part);
        if (type == 'list)
            output = output + ((| .(part[1])(@part.subrange(2)) |) || "");
        else if (type == 'string)
            output = output + part;
    }
    return output;
.

public match_descendants
    arg string;
    var match, child;
    
    match = .match_children(string);
    if (match)
        return match;
    for child in (.children()) {
        match = child.match_descendants(string);
        if (match)
            return match;
    }
    return 0;
.

public chown: disallow_overrides
    arg [new_owners];
    var owner;
    
    (> .perms(sender(), 'manager) <);
    if (new_owners && type(new_owners[1]) == 'list)
        throw(~invarg, "Arguments must be objects.");
    for owner in (new_owners) {
        if (!valid(owner))
            throw(~invowner, "Owner is an invalid object.");
    }
    for owner in (owners || []) {
        if (!valid(owner))
            owners = owners.setremove(owner);
    }
    for owner in ((owners || []).set_difference(new_owners))
        owner.del_owned_obj();
    if (!new_owners || new_owners == [$control]) {
        $control.add_owned_obj();
        (| clear_var('owners) |);
        return;
    }
    for owner in (new_owners.set_difference(owners || []))
        owner.add_owned_obj();
    owners = new_owners;
.

public debug
    arg [stuff];
    var x, line, mngr, meth;
    
    meth = (| callers()[2][3] |);
    if (meth)
        line = "DEBUG " + sender().objname() + "." + tostr(meth) + "(): ";
    else
        line = "DEBUG " + sender().objname() + ": ";
    for x in (stuff)
        line = line + " " + (type(x) == 'string ? x | $data_lib.unparse(x));
    (| .manager().tell(line) |);
.

public set_quota
    arg value;
    
    (> .perms(caller(), $user, @$sys.system(), $root) <);
    quota = value;
.

public name
    arg [args];
    
    return .objname();
.

public del_owner: disallow_overrides
    arg owner, [nodefault];
    
    caller() == definer() || sender() == owner || (> .perms(sender(), 'manager) <);
    owners = (owners || []).setremove(owner);
    owner.del_owned_obj();
    if (!nodefault)
        .add_owner(.manager());
.

public add_owner: disallow_overrides
    arg owner;
    
    caller() == definer() || sender() == owner || (> .perms(sender(), 'manager) <);
    owners = (owners || []).setadd(owner);
    owner.add_owned_obj();
.

public generate_html
    arg [args];
    var actor, name, out, line, obj, col, colx, objs, x;
    
    actor = sender();
    name = .namef('ref);
    out = ["<head><title>" + name + "</title></head>", "<body>", "<h1 align=center><a href=\"/bin/display?" + .objname() + "\">" + name + "</a></h1>"];
    line = [];
    for obj in (.parents())
        line = [@line, $http_lib.make_object_href(obj, obj.namef('xref), "/bin/object?")];
    out = [@out, "<P align=center><b>Parent(s): </b><code>" + $list.to_english(line) + "</code>", "<pre>"];
    
    // duplicate the $code.generate_family_listing code because we
    // want to put in hrefs.
    objs = .children();
    if (objs) {
        out = [@out, "Children:"];
        col = ((| sender().linelen() |) || 79) / 8;
        colx = col * 3;
        line = "Name".pad(colx + 2) + " Perms".pad(col - 2);
        out = [@out, line + "Size ".pad(-col) + "Manager"];
        for obj in (objs) {
            name = obj.objname();
            line = $http_lib.make_object_href(obj, name.pad(colx + 2), "/bin/object?");
            line = line + $object_lib.see_perms(obj).pad(col - 2);
            line = line + tostr(obj.size()).pad(" ", -col);
            x = obj.manager();
            if (!valid(x))
                name = ("** invalid object *(" + tostr(x) + ") **").pad(colx);
            else
                name = $http_lib.make_object_href(x, x.namef('xref).pad(colx));
            line = line + name;
            out = [@out, line];
        }
    }
    out = [@out, "</pre>"];
    return out;
.

public show_in_html: disallow_overrides
    var out, actor, data, parent, info, m, p, x, line, types;
    
    // duplicating code in .show because I want it to be htmlified
    actor = sender();
    data = $sys.sender_data();
    
    // the beginning.
    out = "Object: " + $http_lib.make_object_href(this(), 0, "/bin/object?") + " [" + tostr(.size()) + " bytes]";
    line = [];
    for x in (.parents())
        line = [@line, $http_lib.make_object_href(x)];
    out = [out, "Parent(s): " + $list.to_english(line), "<pre>"];
    
    // Methods
    if (.has_flag('methods, sender())) {
        types = ["Public", "Protected", "Private", "Root", "Driver"];
        info = .method_info(['ancestors_to, this()], [this()], "");
        for m in [1 .. 5] {
            if (info[m]) {
                out = out + [types[m] + " Methods:"];
                for m in (info[m])
                    out = [@out, $object_lib.parse_method_flags(m[4]) + tostr(m[5]).right(4) + " " + $http_lib.make_method_href(m)];
            }
        }
    } else {
        out = out + ["  ** No permission to list Methods **"];
    }
    
    // Parameters
    if (.has_flag('parameters, sender())) {
        for parent in (data) {
            // because the server is FKD we have to make sure your params
            // all have valid parents:
            if (valid(parent[1])) {
                out = out + [parent[1].namef('xref) + " Parameters:"];
                if (parent[1].has_flag('parameters, actor)) {
                    for p in (parent[2]) {
                        line = "  " + tostr(p[1]) + ": " + ($http_lib.filter_text([$data_lib.unparse(p[2])]))[1];
                        out = out + [line];
                    }
                } else {
                    out = out + ["  ** Permission Denied **"];
                }
            } else {
                out = out + ["(Invalid parent-object You cannot reference because  it's broke) Parameters:"];
                for p in (parent[2]) {
                    line = "  " + tostr(p[1]) + ": " + ($http_lib.filter_text([$data_lib.unparse(p[2])]))[1];
                    out = out + [line];
                }
            }
        }
    } else {
        out = out + ["  ** No permission to list Parameters **"];
    }
    
    // Return what we've got.
    return out;
.

public quota
    return quota;
.

public quota_byte_usage
    var total, obj;
    
    // perm checking goes here
    for obj in (.owned())
        total = total + obj.size();
    return total;
.

public _coreify_root
.

root _initialize_ancestor
    arg ancestor;
    var method;
    
    method = tosym("init_" + tostr(ancestor.objname('symbol)));
    catch ~methodnf {
        if (find_method(method) != ancestor)
            throw(~perm, "Initialization method for " + ancestor.objname() + " in wrong place (" + find_method(method).objname() + ")");
        .(method)();
    }
.

root _uninitialize_ancestor
    arg ancestor;
    var method;
    
    method = tosym("uninit_" + tostr(ancestor.objname('symbol)));
    catch ~methodnf {
        if (.find_method(method) != ancestor)
            throw(~perm, "UnInitialization method for " + ancestor.objname() + " in wrong place (" + .find_method(method).objname() + ")");
        .(method)();
    }
.

public objname
    arg [args];
    
    args = [@args, 'string][1];
    switch (args) {
        case 'string:
            return "$" + tostr(objname);
        case 'symbol:
            return objname;
        default:
            throw(~type, "args must be 'string or 'symbol");
    }
.

public set_objname
    arg new_objname;
    var old_objname;
    
    // Only accept calls from owners or admins.
    (> .perms(sender()) <);
    
    // Make sure first argument is a symbol.
    if (type(new_objname) != 'symbol)
        throw(~type, "New objname is not a symbol.");
    
    // Make sure everything is lowercase.
    new_objname = tosym(tostr(new_objname).lowercase());
    
    // Do nothing if new_objname isn't different.
    if (new_objname == objname)
        return;
    
    // Grab the new objname
    (> $sys.assign_objname(new_objname) <);
    old_objname = objname;
    objname = new_objname;
    
    // If we already had a objname, get rid of the old one.
    if (old_objname)
        (> $sys.deassign_objname(old_objname) <);
.

root _show_methods: disallow_overrides
    arg wildcard, actor;
    var output, methods, m, types, type;
    
    methods = .method_info(['ancestors_to, this()], [this()], wildcard);
    types = ["Public", "Protected", "Private"];
    output = [];
    for type in [1 .. 3] {
        if (methods[type]) {
            output = output + [types[type] + " Methods:"];
            for m in (methods[type]) {
                output = output + ["  " + tostr(m[2]) + "(" + m[3] + ")"];
                pause();
            }
        }
    }
    return output;
.

root _show_parameters: disallow_overrides
    arg wildcard, actor, chop, show_invalid;
    var parent, data, output, p, line, len;
    
    output = [];
    data = $sys.sender_data();
    len = actor.linelen();
    for parent in (data) {
        if (valid(parent[1])) {
            output = output + [parent[1].objname() + " Parameters:"];
            if (parent[1].has_flag('parameters, actor)) {
                for p in (parent[2]) {
                    line = "  " + tostr(p[1]) + ": " + $data_lib.unparse(p[2]);
                    if (chop)
                        line = line.chop(len);
                    output = output + [line];
                }
            } else {
                output = output + ["  ** Permission Denied **"];
            }
        } else if (show_invalid) {
            output = output + [$object_lib.get_name(parent[1]) + " Parameters:"];
            for p in (parent[2]) {
                line = "  " + tostr(p[1]) + ": " + $data_lib.unparse(p[2]);
                output = output + [line];
            }
        }
        pause();
    }
    return output;
.

public set_created
    arg value;
    
    created_on = value;
.

public created_on
    return created_on;
.

public is_of: disallow_overrides
    arg obj;
    
    return obj in ancestors();
.

public _object_header_in_html
    arg [args];
    var len, line, out, chop;
    
    chop = [@args, 1][1];
    len = (| sender().linelen() |) || 79;
    out = ["<font size=+1>"];
    line = "Object: " + .namef('xref) + " ";
    line = line + $object_lib.see_perms(this());
    out = [@out, line.left(len / 3 * 2) + " Size: " + .size().to_english()];
    line = .owners();
    if (line.length() > 1)
        line = "Owners: " + line.map('objname).to_english();
    else if (!line)
        line = "Owner: (none)";
    else
        line = "Owner: " + line.namef('xref);
    if (chop)
        line = line.chop(len);
    out = [@out, line];
    line = .parents();
    if (line.length() > 1)
        line = "Parents: " + line.map('objname).to_english();
    else if (!line)
        line = "Parents: (none)";
    else
        line = "Parent: " + line[1].namef('xref);
    if (chop)
        line = line.chop(len);
    out = [@out, line];
    if (.has_ancestor($located)) {
        line = $object_lib.get_name(.location(), 'namef, ['xref]);
        out = [@out, "Location: " + line];
    }
    return out + ["</font>"];
.

public is: disallow_overrides
    arg obj;
    
    return has_ancestor(obj);
.

public hname
    arg [args];
    
    return "<a href=\"/bin/display?" + .objname() + "\">" + .objname() + "</a>";
.

public add_flag: disallow_overrides
    arg flag;
    
    (> .perms(sender(), 'manager) <);
    if (flag == 'core && !$sys.is_system(sender()))
        throw(~perm, "Only system objects can set the 'core flag.");
    flag == 'fertile && (> .perms(sender(), 'manager) <);
    
    // let them add any flag they want
    flags = (flags || []).setadd(flag);
.

public clear_variables
    arg [variables];
    var v;
    
    (> .perms(sender()) <);
    for v in (variables)
        (| .clear_variable(v) |);
.

public clear_variable: disallow_overrides
    arg name;
    var n, obj;
    
    (> .perms(sender()) <);
    n = tosym("_clear_var_" + tostr(time()));
    catch any {
        .compile(["clear_var(" + toliteral(name) + ");"], n);
        for obj in (.descendants()) {
            (| obj.(n)() |);
            pause();
        }
        (| del_method(n) |);
    } with handler {
        (| del_method(n) |);
    }
.

public ancestors_to
    arg obj;
    var a, out;
    
    out = [this()];
    for a in (.ancestors()) {
        if (a.has_ancestor(obj))
            out = out.setadd(a);
    }
    return out;
.

public descendants_to: disallow_overrides
    arg checked, levels, maxlev;
    var c, list;
    
    list = [this()];
    levels = levels + 1;
    
    // check parents
    if (!maxlev || maxlev != levels) {
        for c in (.children()) {
            list = list + [c.descendants_to(checked, levels, maxlev)];
            checked = checked.setadd(c);
        }
    }
    return list;
.

public ancestry
    arg gen;
    var i, out;
    
    out = [];
    if (type(gen) == 'dbref) {
        for i in (.ancestors()) {
            if (i.has_ancestor(gen))
                out = [@out, i];
        }
        return out;
    }
    if (gen != 0) {
        for i in (.parents())
            out = out.union(i.ancestry(gen - 1));
    }
    return out;
.

public generations
    arg gen;
    var p, out;
    
    out = [this()];
    if (gen != 0) {
        for p in (.parents())
            out = out.union(p.generations(gen - 1));
    }
    return out;
.

public variable_info
    arg [args];
    var data, pattern, gen, match, definers, ancs, pparams, param, filter;
    
    // data() reformated as
    // [[$parent,'param,val],...]
    if (!.has_flag('parameters, sender()))
        throw(~perm, "Parameters on " + .objname() + " are not readable by " + sender().objname());
    if (args) {
        definers = args[2];
        filter = args[3];
    } else {
        filter = "";
    }
    if (definers)
        gen = ['ancestors_to, definers[1]];
    else if (!gen)
        gen = ['generations, 1];
    ancs = .(gen[1])(gen[2]);
    if (definers)
        ancs = $list.intersection(ancs, definers);
    if (!ancs)
        ancs = this();
    data = [];
    match = (| .setting("match-with") |) || 'match_regexp;
    for pparams in ($list.reverse($sys.sender_data())) {
        if (valid(pparams[1]) && pparams[1] in ancs) {
            for param in (pparams[2]) {
                if ($string.(match)(filter, tostr(param[1])))
                    data = [@data, [pparams[1], @param]];
            }
        }
    }
    return data;
.

public method_flags: disallow_overrides, synchronized
    arg method;
    
    if (!.has_flag('methods, sender()))
        throw(~perm, sender().objname() + " doesn't have permission to find methods on " + .objname());
    return (> method_flags(method) <);
.

public method_state: disallow_overrides, synchronized
    arg method;
    
    if (!.has_flag('methods, sender()))
        throw(~perm, sender().objname() + " doesn't have permission to find methods on " + .objname());
    return (> method_state(method) <);
.

public set_method_flags: disallow_overrides, synchronized
    arg method, flags;
    
    if (!.is_writable_by(sender()))
        throw(~perm, sender().objname() + " cannot write to " + .objname());
    if ('locked in flags && !$sys.is_system(sender()))
        throw(~perm, "Only administrators can set the locked method flag.");
    return (> set_method_flags(method, flags) <);
.

public set_method_state: disallow_overrides, synchronized
    arg method, state;
    
    if (!.is_writable_by(sender()))
        throw(~perm, sender().objname() + " cannot write to " + .objname());
    return (> set_method_state(method, state) <);
.

public method_args
    arg method;
    
    if (!.has_flag('methods, sender()))
        throw(~perm, sender().objname() + " doesn't have permission to find methods on " + .objname());
    return (> method_args(method) <);
.

public set_flags: disallow_overrides
    arg new_flags;
    
    (> .perms(sender(), 'manager) <);
    if (type(new_flags) != 'list)
        throw(~invflags, "Flags must be submitted as a list of symbols.");
    if (!new_flags && flags)
        return clear_var('flags);
    if ('core in new_flags && !$sys.is_system(sender()))
        throw(~perm, "Only system objects can set the 'core flag.");
    'fertile in new_flags && (> .perms(sender, 'manager) <);
    flags = new_flags;
.

public managed: disallow_overrides
    return managed || [];
.

root add_managed_obj: disallow_overrides
    managed = (managed || []).setadd(sender());
.

root del_managed_obj: disallow_overrides
    managed = (managed || []).setremove(sender());
    if (!managed)
        clear_var('managed);
.

public writes: disallow_overrides
    (> .perms(sender(), 'trusts) <);
    return writes || [];
.

root add_writable_obj: disallow_overrides
    writes = (writes || []).setadd(sender());
.

root del_writable_obj: disallow_overrides
    writes = (writes || []).setremove(sender());
    if (!writes)
        clear_var('writes);
.

public trusted_by: disallow_overrides
    return trusted_by || [];
.

root add_trusted_obj: disallow_overrides
    trusted_by = (trusted_by || []).setadd(sender());
.

root del_trusted_obj: disallow_overrides
    trusted_by = (trusted_by || []).setremove(sender());
    if (!trusted_by)
        clear_var('trusted_by);
.

public has_flag: disallow_overrides
    arg flag, [sender];
    
    if (flag == 'core)
        return flag in .flags();
    return flag in .flags() || .trusts([@sender, sender()][1]);
.

public flags: disallow_overrides
    return flags || [];
.

root clean_root
    var obj, type, list;
    
    for type in (['owners, 'owned, 'managed, 'writes]) {
        if (list = get_var(type)) {
            list = list.valid_objects();
            if (!list)
                clear_var(type);
            else
                set_var(type, list);
        }
    }
.

public _clean_database: disallow_overrides
    var obj, iobjs, vobjs, icount, result, name;
    
    (> .perms(sender(), $sys) <);
    for obj in (.descendants()) {
        if (!valid(obj)) {
            iobjs = iobjs + 1;
        } else {
            obj.clean_root();
            vobjs = vobjs + 1;
            name = tosym("clean_" + tostr(obj.objname('symbol)));
            catch ~methodnf {
                def = find_method(name);
                if (def != obj) {
                    icount = icount + 1;
                } else {
                    result = (| obj.(name)() |);
                    (| $sys.log(result) |);
                }
            }
        }
    
        // incase we need more ticks
        pause();
    }
    return [iobjs, vobjs, icount];
.



parent $root
object $foundation

var $root child_index 5
var $root fertile 1
var $root trusted []
var $root inited 1
var $root objname 'foundation
var $root created_on 796268969
var $root indestructable 1
var $root flags ['parameters, 'methods, 'code, 'fertile, 'core]

public configure
    // it is intended that this method is called just after an object is created
    // descended methods should pass() up the line.  If something which should
    // be set, is already set, do NOT re-set it again.  Blank returns at prompts
    // should always be taken as a skip request, along with "skip" and "@skip"
    (> .perms(sender()) <);
.



parent $root
object $core

var $root objname 'core
var $root child_index 4
var $root inited 1
var $root created_on 796268969
var $root indestructable 1
var $root flags ['parameters, 'methods, 'code, 'core]



parent $foundation
object $event_handler

var $root inited 1
var $root objname 'event_handler
var $root created_on 796268969
var $root indestructable 1
var $root flags ['parameters, 'methods, 'code, 'core]

public event
    arg frob;
    
.

public announce_event
    arg frob;
    var x;
    
    for x in (.environment())
        (| x.event(frob) |);
.



parent $foundation
object $has_commands

var $root fertile 1
var $root inited 1
var $root objname 'has_commands
var $root created_on 796268969
var $root indestructable 1
var $root flags ['parameters, 'methods, 'code, 'fertile, 'core]
var $has_commands commands 0
var $has_commands shortcuts #[]
var $has_commands local 0
var $has_commands remote 0

public add_command
    arg template, method, [type];
    var cmd, types, count, x;
    
    (> .perms(sender()) <);
    type = [@type, 'local][1];
    if ("*" in template)
        throw(~invcmd, "Invalid command, command templates cannot contain \"*\"!.");
    cmd = (> $command_lib.validate_command_template(template) <);
    if (!(type in ['local, 'remote]))
        throw(~type, "Command types can be either 'local or 'remote");
    if ('this in cmd[2].values().slice(1))
        type = 'remote;
    if (type == 'remote) {
        for x in (cmd[2].values().slice(1)) {
            if (x == 'this)
                count = count + 1;
        }
        if (!count)
            throw(~add_command, "Command type defined as remote with no <this> argument.");
        else if (count > 1)
            throw(~add_command, "More than one <this> argument specified in template.");
    }
    if (!get_var(type))
        set_var(type, #[]);
    set_var(type, get_var(type).add_elem_union(cmd[1][1], [@cmd[1], template, method, cmd[2]]));
.

public all_local_commands
    var cmds, a, acmds;
    
    cmds = #[];
    for a in (ancestors()) {
        if (a == definer())
            break;
        if (acmds = (| a.local_commands() |))
            cmds = cmds.add(a, acmds);
    }
    return cmds;
.

public local_commands
    return local || #[];
.

public get_command_info
    arg type, cmd;
    var info, a, ainfo;
    
    info = [];
    for a in (ancestors()) {
        if (a == definer())
            break;
        if (ainfo = (| a.command_info(type, cmd) |))
            info = info.union(ainfo);
    }
    return info;
.

public del_command
    arg template, method;
    var cmd, c, d, info, type;
    
    (> .perms(sender()) <);
    cmd = template.explode();
    if (!cmd)
        throw(~type, "Invalid template.");
    cmd = cmd[1];
    info = #[['local, .get_command_info('local, cmd)]];
    info = info.add('remote, .get_command_info('remote, cmd));
    for type in (info) {
        for c in (type[2]) {
            if (c[3] == template && c[4] == method) {
                set_var(type[1], get_var(type[1]).del_elem(cmd, c));
                d = d + 1;
            }
        }
    }
    return d;
.

public remote_commands
    return remote || #[];
.

root init_has_commands
    local = remote = shortcuts = #[];
.

root uninit_has_commands
    .clear_variables(@.parameters());
.

public add_shortcut
    arg shortcut, template, method;
    var relation;
    
    (> .perms(sender()) <);
    if (type(shortcut) != 'string || type(template) != 'string)
        throw(~type, "Both shortcut and template must be strings.");
    if (type(method) != 'symbol)
        throw(~type, "Method must be submitted as a symbol.");
    relation = (> $command_lib.parse_relation(shortcut, template) <);
    if (!shortcuts)
        shortcuts = #[];
    shortcuts = shortcuts.add(pattern, [method, relation]);
.

public del_shortcut
    arg shortcut;
    var value;
    
    (> .perms(sender()) <);
    value = (| shortcuts.del(shortcut) |);
    if (type(value) != 'dictionary)
        throw(~shortcutnf, "Shortcut \"" + shortcut + "\" is not defined on this object.");
    shortcuts = value;
.

public all_shortcuts
    var s, a, as;
    
    s = [];
    for a in (ancestors()) {
        if (a == definer())
            break;
        if (as = (| a.shortcuts() |))
            s = [@s, @as.to_list()];
    }
    return s;
.

public get_shortcut_info
    arg shortcut;
    
    return (| shortcuts[shortcut] |) || throw(~shortcutnf, "Shortcut \"" + shortcut + "\" is not defined on this object.", shortcut);
.

public coreify_has_commands
    var line;
    
    (> .perms(caller(), $sys) <);
    line = "commands = " + toliteral(commands) + ";";
    line = line + "shortcuts = " + toliteral(shortcuts) + ";";
    .compile([line], 'CORE_EVAL);
    commands = shortcuts = 0;
.

public shortcuts
    return shortcuts || #[];
.

public command_info
    arg type, cmd;
    
    return (| get_var(type)[cmd] |) || throw(~cmdnf, "Command \"" + cmd + "\" is not defined on this object.", cmd);
.

public all_remote_commands
    var cmds, a, acmds;
    
    cmds = #[];
    for a in (ancestors()) {
        if (a == definer())
            break;
        if (acmds = (| a.remote_commands() |))
            cmds = cmds.add(a, acmds);
    }
    return cmds;
.



parent $foundation
object $environment

var $root inited 1
var $root objname 'environment
var $root created_on 796268969
var $root indestructable 1
var $root flags ['parameters, 'methods, 'code, 'core]

public environment
    return [];
.

public match_environment
    arg str;
    var obj, env, found, match;
    
    if (!str)
        throw(~objnf, "No object specified.", str);
    str = str.strip_article();
    
    // Handle special cases.
    if (str == "me")
        return this();
    else if (str[1] == "$" || str[1] == "#")
        return (> $object_lib.to_dbref(str) <);
    else if (str in ["it", "him", "her"])
        return (| .match_context(str) |) || throw(~context, "I don't see " + str + " here, do you?");
    
    // Ok, do the long matching process...
    found = [];
    for obj in (.environment()) {
        if (obj.name('noarticle) == str)
            return obj;
        if (match = obj.match_name(str))
            found = found + [[obj, match]];
    }
    if (!found)
        throw(~objnf, "No \"" + str + "\" in your environment.", str);
    if (found.length() == 1) {
        return found[1][1];
    } else {
        match = found.slice(2);
        if (match.count(found.slice(2)) == 1)
            return found[match in found.slice(2)][1];
        else
            throw(~ambig, "\"" + str + "\" can match " + found.slice(1).map('name).to_english("", " or ") + ".");
    }
.

public local_to_environment
    arg obj;
    
    return obj in .environment();
.



parent $foundation
object $has_messages

var $root fertile 1
var $root inited 1
var $root objname 'has_messages
var $root created_on 796268969
var $root indestructable 1
var $root flags ['parameters, 'methods, 'code, 'fertile, 'core]
var $has_messages messages 0
var $has_messages message_info 0

public add_message
    arg name;
    
    (> .perms(sender(), 'writer) <);
    if (type(name) != 'string)
        throw(~type, "Name must be a string");
    if (!messages)
        messages = #[];
    if (!message_info)
        message_info = #[];
    message_info = message_info.add(name, #[]);
.

public message_info
    arg name, field;
    var m;
    
    catch ~keynf {
        m = message_info[name];
    } with handler {
        throw(~messagenf, "Message " + name + " is not defined here.");
    }
    switch (field) {
        case 'evaluator:
            return (| m['evaluator] |) || $cml2_base_eval;
        case 'compiler:
            return (| m['compiler] |) || $cml2_compiler;
        case 'uncompiler:
            return (| m['uncompiler] |) || $cml2_uncompiler;
        default:
            throw(~fieldnf, "Invalid field.");
    }
.

public set_message_info
    arg name, field, value;
    var m;
    
    catch ~keynf {
        m = message_info[name];
    } with handler {
        throw(~messagenf, "Message " + name + " is not defined here.");
    }
    m = m.add(field, value);
    message_info = message_info.add(name, m);
.

public message_parts
    arg name;
    
    return (| message_info[name][4] |) || [];
.

public evalutor
    arg name;
    
    return defined_messages[name][1];
.

public set_evaluator
    arg name, evaluator;
    var current;
    
    .perms(sender(), 'writer);
    current = defined_messages[name];
    current = current.replace(1, evaluator);
    .set_message_info(name, current);
.

public defines_message
    arg name;
    var n;
    
    // returns 1 if the message <name> is defined here.
    if (message_info) {
        for n in (message_info.keys()) {
            if (name == n)
                return 1;
        }
    }
    return 0;
.

public del_message
    arg name;
    var mess, kids;
    
    (> .perms(sender(), 'writer) <);
    message_info = message_info.del(name);
.

public local_messages
    return messages || #[];
.

public messages
    var all_messages, a, a_messages, d_messages, d, m, my_d;
    
    // return all messages set on this() or parents
    // a : on eancestor
    // all_messages: the sum total of message
    // a_messages: messages from ancestor a
    // d: the definer of a message
    // d_messages: messages from ancestor a, defined on d
    // m: a specific message
    // my_d: messags from definer d that have already been found.
    all_messages = #[];
    for a in (.ancestors()) {
        if (a == definer())
            break;
        if (a.has_ancestor(definer())) {
            a_messages = a.local_messages();
            for d in (a_messages.keys()) {
                d_messages = a_messages[d];
                my_d = (| all_messages[d] |) || #[];
                for m in (d_messages.keys()) {
                    if (!(m in my_d.keys()))
                        my_d = my_d.add(m, d_messages[m]);
                }
                all_messages = all_messages.add(d, my_d);
            }
        }
    }
    return all_messages;
.

public local_message
    arg name, [definer];
    var d;
    
    if (messages) {
        if (!definer) {
            for d in (messages.keys()) {
                catch ~keynf {
                    return messages[d][name];
                }
            }
        } else {
            catch ~keynf {
                return messages[definer[1]][name];
            }
        }
    }
    throw(~messagenf, "Message was not found.");
.

public prep_evaluator
    arg name;
    
    .perms(sender(), 'writers);
    return (.messaage_info(name))[2];
    
    // 9-26-95/19:57 Jeff ($jeff), moved from $has_messages.prep_evalutor
.

public message
    arg name, [definer];
    var a, message, mes, m, empty;
    
    //retrieve the specified message as ctext
    if (definer)
        definer = definer[1];
    else
        definer = (._find_message_definer(name))[2];
    message = $message_frob.new(#[]);
    empty = $ctext_frob.new("");
    for a in (.ancestors()) {
        catch ~methodnf, ~messagenf {
            return a.local_message(name, definer);
        }
    }
    throw(~messagenf, "No matching message.");
.

public set_message
    arg name, message, [definer];
    var mes, partial, compiler;
    
    (> .perms(sender(), 'writer) <);
    definer = ._find_message_definer(name);
    partial = definer[1];
    definer = definer[2];
    if (!messages)
        messages = #[];
    mes = (| messages[definer] |) || #[];
    compiler = definer.message_info(partial, 'compiler);
    message = compiler.compile_cml(message);
    messages = messages.add(definer, mes.add(name, message));
.

public local_matching_messages
    arg name, [definer];
    var n, matches, d;
    
    matches = [];
    if (definer) {
        for n in (messages[definer].keys()) {
            if ($string.match_begin(n, name))
                matches = [@matches, n];
        }
    } else {
        for d in (messages.keys()) {
            for n in (messages[d].keys()) {
                if ($string.match_begin(n, name))
                    matches = [@matches, n];
            }
        }
    }
    return matches;
.

public matching_messages
    arg name, definer;
    var mes, a, m, len, messages, found, anc;
    
    mes = $message_frob.new(#[]);
    found = [];
    
    //if (messages)
    //  return mes;
    name = name + ".";
    len = name.length();
    anc = .ancestors();
    a = $has_messages in anc;
    anc = anc.subrange(1, a - 1);
    for a in (anc) {
        messages = (| (a.local_messages())[definer] |) || #[];
        for m in (messages.keys()) {
            if ($string.match_begin(m, name) && !(m in found))
                mes = mes.add_entry(m.subrange(len + 1), messages[m]);
            found = [@found, m];
        }
    }
    return mes;
.

public _del_message
    arg name;
    var mess;
    
    mess = messages[sender()];
    mess = mess.del(name);
    messages = messages.add(sender(), mess);
.

public _find_message_definer
    arg name;
    var a, pos, name2;
    
    pos = name.rindex(".");
    if (pos)
        name2 = name.subrange(1, pos - 1);
    for a in (.ancestors()) {
        catch ~methodnf {
            if (a.defines_message(name))
                return [name, a];
            else if (a.defines_message(name2))
                return [name2, a];
        }
    }
    throw(~definernf, "Could not find definer for " + name + ".");
.

public eval_message
    arg name, vars, [definer];
    var message, partial;
    
    definer = [@definer, 0][1];
    if (!definer) {
        definer = ._find_message_definer(name);
        partial = definer[1];
        definer = definer[2];
    } else {
        partial = name;
    }
    vars = vars.add('evaluator, definer.message_info(partial, 'evaluator));
    message = .matching_messages(partial, definer);
    if (message == (<$message_frob, #[]>))
        message = $message_frob.new(#[["general", .message(partial, definer)]]);
    message = message.set_vars(vars);
    return message.eval_ctext(vars.add('time, 'pre));
.

root uninit_has_messages
    messages = 0;
    defined_messages = 0;
.

public _del_message_part
    arg name, part;
    var m, mess;
    
    .debug(sender(), name, part);
    if (messages && sender() in messages.keys()) {
        mess = messages[sender()];
        .debug(mess);
        if (name in mess.keys()) {
            m = mess[name];
            .debug(m);
            m = m.del_entry(part);
            .debug(m);
            mess = mess.add(name, m);
            messages = messages.add(sender(), mess);
        }
    }
.

public unset_message
    arg message, definer;
    var mes;
    
    mes = (| messages[definer] |) || #[];
    mes = mes.del(message);
    messages = messages.add(definer, mes);
.

public set_var
    arg this, name, value;
    var vars;
    
    vars = this[2];
    vars = vars.add(name, value);
    return (<this(), [this[1], vars]>);
.

public set_vars
    arg this, new;
    var vars, key;
    
    vars = this[2];
    for key in (new)
        vars = vars.add(key, new[key]);
    return (<this(), [this[1], vars]>);
.



parent $foundation
object $public

var $root fertile 1
var $root inited 1
var $root objname 'public
var $root created_on 796268969
var $root indestructable 1
var $root flags ['parameters, 'methods, 'code, 'fertile, 'core]
var $public public []

public is_publicly
    arg what;
    
    return (| what in public |) || 0;
.

public set_public
    arg what;
    var x, valid;
    
    if (sender() != this())
        (> .perms(sender(), 'manager) <);
    if (!public)
        public = [];
    if (type(what) == 'symbol) {
        if (what in public)
            return;
        what = [@public, what];
    }
    if (type(what) != 'list)
        throw(~type, toliteral(what) + " is an invalid type.");
    public = what;
.

public public
    return public;
.



parent $foundation
object $network

var $root objname 'network
var $root child_index 3
var $root created_on 809051864
var $root inited 1
var $root indestructable 1
var $root flags ['parameters, 'methods, 'code, 'core]

public reassign_connection
    arg [args];
    
    return (> reassign_connection(@args) <);
.

public hostname
    arg [args];
    
    return (> hostname(@args) <);
.

public ip
    arg [args];
    
    return (> ip(@args) <);
.



parent $foundation
object $help_root

var $root inited 1
var $root objname 'help_root
var $root created_on 796268969
var $root indestructable 1
var $root flags ['parameters, 'methods, 'code, 'core]



parent $foundation
object $text

var $root fertile 1
var $root inited 1
var $root objname 'text
var $root created_on 796268969
var $root indestructable 1
var $root flags ['parameters, 'methods, 'code, 'fertile, 'core]
var $text text 0

root init_text
    text = [];
.

root uninit_text
    text = [];
.

public text
    // returns text
    if (!.is_writable_by(sender()) && sender() != this())
        throw(~perm, "Permission Denied.");
    return text;
.

public set_text
    arg txt;
    
    // resets ,text to the list sent
    if (!.is_writable_by(sender()) && sender() != this())
        throw(~perm, "Permission Denied.");
    text = txt;
.

public ins_line
    arg txt, [loc];
    
    // inserts txt at loc (where loc is an integer)
    if (!.is_writable_by(sender()) && sender() != this())
        throw(~perm, "Permission Denied.");
    if (!loc)
        text = [@text, txt];
    else
        text = (> text.insert(loc, txt) <);
.

public del_text
    // deletes all text
    if (!.is_writable_by(sender()) && sender() != this())
        throw(~perm, "Permission Denied.");
    text = [];
.

public del_line
    arg linestr;
    
    // deletes "line" where line is the actual line to delete
    if (!.is_writable_by(sender()) && sender() != this())
        throw(~perm, "Permission Denied.");
    text = text.setremove(line);
.

public del_nline
    arg nline;
    
    // deletes nline where nline is an integer reference to a list location
    if (!.is_writable_by(sender()) && sender() != this())
        throw(~perm, "Permission Denied.");
    text = (> text.delete(nline) <);
.

public ins_lines
    arg lines, loc;
    var line;
    
    // inserts txt at loc (where loc is an integer)
    if (!.is_writable_by(sender()) && sender() != this())
        throw(~perm, "Permission Denied.");
    if (type(lines) != 'list)
        throw(~type, "Lines should be passed as a list of strings.");
    for line in (lines) {
        text = (> text.insert(loc, line) <);
        loc = loc + 1;
    }
.



parent $foundation
object $messaged

var $root fertile 1
var $root inited 1
var $root objname 'messaged
var $root created_on 796268969
var $root indestructable 1
var $root flags ['parameters, 'methods, 'code, 'fertile, 'core]
var $messaged messages 0

public messages
    .perms(sender(), 'this);
    return messages || #[];
.

public all_messages
    var msgs, anc, all;
    
    // For message to call when inheriting, or something
    .perms(sender(), 'this);
    all = #[];
    for anc in (.ancestors()) {
        msgs = (| anc.messages() |);
        if (msgs)
            all = all.union(msgs);
        if (anc == definer())
            break;
    }
    return all;
.

public message
    arg id;
    var ret;
    
    // Don't check .all_messages, we should get rid of it.  Methods
    // calling .message should have their own defaults.
    // ret = (| (.all_messages())[id] |);
    return (| (.messages())[id] |) || 0;
.

public add_message
    arg id, msg;
    
    (> .perms(sender()) <);
    if (!messages)
        messages = #[];
    if (.has_message(id))
        throw(~msgexists, "Message already defined.");
    messages = messages.add(id, msg);
.

public set_message
    arg id, msg;
    
    (> .perms(sender()) <);
    if (!messages)
        messages = #[];
    if (.defines_msg(id))
        messages = messages.replace(id, msg);
    else
        .add_message(id, msg);
.

public has_message
    arg id;
    
    return id in .all_messages().keys();
.

public defines_msg
    arg id;
    
    return id in .messages().keys();
.

root init_messaged
    messages = #[];
.



parent $foundation
object $has_settings

var $root child_index 1
var $root fertile 1
var $root inited 1
var $root flags ['parameters, 'methods, 'code, 'fertile, 'core]
var $root indestructable 1
var $root objname 'has_settings
var $root created_on 796268969
var $has_settings settings #[]
var $has_settings defined_settings #[]
var $has_settings local_settings []

root uninit_has_settings
    settings = 0;
    defined_settings = 0;
    local_settings = 0;
.

public _find_definers
    arg name, count, [begin];
    var a, matches;
    
    // Find a definer for <name>
    // <count> is a number
    //    1 finds the first possible definer
    //    0 finds all
    // If <begin> is given cound a match_begin as a match
    matches = [];
    for a in (ancestors()) {
        if (a.has_ancestor($has_settings)) {
            if (a.defines_setting(name, begin)) {
                switch (count) {
                    case 0:
                        matches = [@matches, a];
                    case 1:
                        return [@matches, a];
                    default:
                        matches = [@matches, a];
                        count = count - 1;
                }
            }
        }
    }
    return matches;
.

public set_setting
    arg definer, name, value;
    var options;
    
    (> .perms(sender(), 'writers) <);
    if (type(name) != 'string)
        throw(~type, "name must be a string");
    if (!definer)
        definer = ._find_setting_definer(name);
    options = definer.configure_setting(name);
    value = (> .((| options['check] |) || 'is_any)(definer, value, @(| options['check_args] |) || []) <);
    (> .((| options['set] |) || 'set_local_setting)(definer, name, value, sender(), @(| options['set_args] |) || []) <);
    if (!local_settings)
        local_settings = [];
    local_settings = local_settings.union([name]);
.

public add_setting
    arg name, [options];
    
    (> .perms(sender(), 'writers) <);
    if (type(name) != 'string)
        throw(~type, "name must be a string");
    options = [@options, #[]][1];
    if (!defined_settings)
        defined_settings = #[];
    if (defined_settings.contains(name))
        throw(~error, .objname() + " already has a setting named " + name);
    defined_settings = defined_settings.add(name, ._default_options());
    .configure_setting(name, options);
.

public defines_setting
    arg name;
    
    if (name in defined_settings.keys())
        return 1;
    return 0;
.

public del_setting
    arg name;
    var k, m;
    
    (> .perms(sender(), 'writers) <);
    if (!(name in defined_settings.keys()))
        throw(~namenf, name + " not defined on " + .dbref());
    m = defined_settings[name]['del];
    defined_settings = defined_settings.del(name);
    for k in (.children())
        (| k.m(this(), name) |);
.

public is_boolean
    arg definer, value, [args];
    
    if (!value || value in ["y", "yes", "true", "t", "1", "on"])
        return 1;
    return 0;
.

public get_inherited_setting
    arg name, definer, [args];
    var sets, a;
    
    for a in (.ancestors()) {
        if (a.has_ancestor(definer) && a.local_settings(definer))
            return a.get_local_setting(definer, name);
    }
    return 0;
.

public get_indirect_setting
    arg name, definer, args;
    var fname, val;
    
    fname = ._to_fullname(name, definer);
    if (fname in setting_data.keys()) {
        val = setting_data[fname];
        if (type(val) == 'dbref)
            return (> setting_data[fname].get_setting(spec, definer, args) <);
        else
            return setting_data[fname];
    } else {
        return (definer.setting_data())[fname];
    }
.

public setting
    arg name, [definer];
    var sets, options;
    
    definer = [@definer, 0][1];
    if (type(name) != 'string)
        throw(~type, "name must be a string");
    if (type(definer) != 'dbref && definer != 0)
        throw(~type, "definer must be zero or a dbref");
    if (!name && !definer)
        throw(~huh, "definer must be non-zero or name must be given");
    if (!definer) {
        definer = ._find_setting_definer(name);
        if (!definer)
            throw(~definernf, "No definer could be found for " + name);
    } else if (!name) {
        sets = #[];
        for name in (definer.defined_settings()) {
            options = definer.configure_setting(name);
            catch ~keynf {
                .(options['access_check])(sender());
            }
            sets = sets.add(name, .((| options['get] |) || 'get_local_method)(name, definer, @(| options['get_args] |) || []));
        }
        return sets;
    }
    options = definer.configure_setting(name);
    return (> .((| options['get] |) || 'get_local_setting)(name, definer, @(| options['get_args] |) || []) <);
.

public default_setting_info
    return #[['type, "string"], ['check, 'is_anything], ['get, 'get_direct_setting], ['set, 'set_direct_setting], ['del, 'del_direct_setting], ['set_args, ['name, 'definer, 'value, 'style]], ['get_args, ['name, 'definer]]];
.

public _find_setting_definer
    arg name;
    var a;
    
    for a in (.ancestors()) {
        if (a.has_ancestor(definer()) && name in a.defined_settings())
            return a;
    }
    throw(~definernf, "Unable to find setting " + toliteral(name) + " on " + .name() + ".");
.

public _default_options
    return #[['get, 'get_local_setting], ['set, 'set_local_setting], ['check, 'is_any], ['del, 'delete_local_setting], ['check_args, []], ['get_args, []], ['set_args, []]];
.

public configure_setting
    arg name, [options];
    var o, new;
    
    //configure options for a setting
    // check perms and types
    if (type(name) != 'string)
        throw(~type, "name must be a string.");
    if (options)
        options = options[1];
    else
        options = #[];
    if (type(options) != 'dictionary)
        throw(~type, "options must be a dictionary.");
    
    //make sure the name exists
    if (!(name in defined_settings.keys()))
        throw(~namenf, "Setting " + name + " is not defined on " + .objname());
    
    //If no options were given return the current options
    if (!options)
        return defined_settings[name];
    
    //ok, now iterate through the options and make changes.
    (> .perms(sender(), 'writers) <);
    new = defined_settings[name];
    for o in (options.keys())
        new = new.add(o, options[o]);
    defined_settings = defined_settings.add(name, new);
.

public defined_settings
    return (| defined_settings.keys() |) || [];
.

public delete_local_setting
    arg definer, name;
    var sets;
    
    (> .perms(sender(), this()) <);
    sets = (| defined_settings[definer] |) || #[];
    sets = (| sets.del(name) |) || sets;
    defined_settings = sets;
.

public is_any
    arg definer, value, [args];
    
    return value;
.

public set_local_setting
    arg definer, name, value, [args];
    var sets;
    
    (> .perms(sender(), 'this) <);
    if (!settings)
        settings = #[];
    sets = (| settings[definer] |) || #[];
    sets = sets.add(name, value);
    settings = settings.add(definer, sets);
.

public get_local_setting: disallow_overrides
    arg name, definer, [args];
    
    return (| settings[definer][name] |) || throw(~setting, "Setting \"" + name + "\" has not been defined.");
.

root init_has_settings
    defined_settings = #[];
    local_settings = #[];
    settings = #[];
.

public settings
    var a, out, s;
    
    //returns all settings available on this object
    out = #[];
    for a in (.ancestors()) {
        if (a == definer())
            break;
        s = (| a.defined_settings() |) || [];
        if (s)
            out = out.add(a, s);
    }
    return out;
.

public is_list_of
    arg definer, value, type;
    var out, element;
    
    switch (type(value)) {
        case 'string:
            if (value[1] != "[" || value.last() != "]")
                throw(~type, "value is not parsable as a list.");
            value = value.subrange(2, value.length() - 2).explode(",");
            .debug(value);
            out = [];
            for element in (value)
                out = [@out, .is_type(definer, element.strip(" "), type)];
            return out;
        case 'list:
            out = [];
            for element in (value)
                out = [@out, .is_type(definer, element, type)];
            return out;
        default:
            throw(~type, "value is not parsable as list.");
    }
.

public is_type
    arg definer, value, type;
    
    if (type(value) == type)
        return value;
    switch (type) {
        case 'string:
            return toliteral(value);
        case 'integer:
            if (value.is_numeric())
                return toint(value);
            else
                throw(~type, "Value is unparseable as integer.");
        case 'dbref:
            return $object_lib.to_dbref(value);
        default:
            return value;
    }
.

public local_setting
    arg [definer];
    
    definer = [definer[1], 0][1];
    if (definer)
        return (| local_settings[definer] |) || [];
    return local_settings;
.

public local_settings
    arg [definer];
    
    definer = [@definer, 0][1];
    if (definer)
        return (| local_settings[definer] |) || [];
    return local_settings;
.

public display_setting
    arg name, [definer];
    var sets, options, s;
    
    (> .perms(sender(), 'trusts) <);
    definer = [@definer, 0][1];
    if (type(name) != 'string)
        throw(~type, "name must be a string");
    if (type(definer) != 'dbref && definer != 0)
        throw(~type, "definer must be zero or a dbref");
    if (!name && !definer)
        throw(~huh, "definer must be non-zero or name must be given");
    if (!definer) {
        definer = ._find_setting_definer(name);
        if (!definer)
            throw(~definernf, "No definer could be found for " + name);
    } else if (!name) {
        sets = #[];
        for name in (definer.defined_settings()) {
            options = definer.configure_setting(name);
            catch ~keynf {
                .(options['access_check])(sender());
            }
            s = .((| options['get] |) || 'get_local_method)(name, definer, @(| options['get_args] |) || []);
            sets = sets.add(name, (| .(options['display])(s) |) || s);
        }
        return sets;
    }
    options = definer.configure_setting(name);
    s = (> .((| options['get] |) || 'get_local_setting)(name, definer, @(| options['get_args] |) || []) <);
    return (| .(options['display])(s) |) || $data_lib.unparse(s, #[['dbref, ['namef, ['ref]]]]);
.

public display_boolean_yes_no
    arg value;
    
    if (value)
        return "yes";
    else
        return "no";
.

public display_boolean_on_off
    arg value;
    
    if (value)
        return "on";
    else
        return "off";
.



parent $has_settings
object $named

var $root child_index 1
var $root fertile 1
var $root inited 1
var $root objname 'named
var $root created_on 796268969
var $root indestructable 1
var $root flags ['parameters, 'methods, 'code, 'fertile, 'core]
var $named name ['normal, "Generic Named Object.", "a Generic Named Object."]
var $named name_aliases 0
var $has_settings defined_settings #[]
var $has_settings local_settings #[]
var $has_settings settings #[]

public set_name
    arg new_name, [args];
    var type;
    
    (> .perms(sender()) <);
    if (new_name && new_name[1] in ["$", "#"])
        throw(~invname, "Names cannot begin with \"$\" or \"#\".");
    if (type(new_name) != 'string)
        throw(~type, "New name must be given as a string.");
    
    // this will not catch them all, but we can try.
    if ("^(a|an|the)\\s+".match_regexp(new_name))
        throw(~bad_name, "Do not include articles in name.");
    type = [@args, 'normal][1];
    if (!(type in ['prop, 'normal, 'uniq]))
        throw(~invarg, "Type must be one of: 'prop, 'normal or 'uniq");
    switch (type) {
        case 'prop:
            new_name = [new_name, new_name];
        case 'uniq:
            new_name = [new_name, "the " + new_name];
        case 'normal:
            new_name = [new_name, new_name.a_or_an() + " " + new_name];
    }
    name = [type, @new_name];
.

public name_aliases
    return name_aliases;
.

public add_name_alias
    arg alias;
    
    .perms(sender());
    name_aliases = [@name_aliases, alias];
.

public del_name_alias
    arg alias;
    
    .perms(sender());
    name_aliases = name_aliases.setremove(alias);
.

root init_named
    var objname;
    
    objname = tostr(.objname('symbol));
    name = ['proper, tostr(objname), tostr(objname)];
    name_aliases = [];
.

public match_name
    arg str;
    
    return .match_name_exact(str) || .match_name_aliases(str);
.

public name
    arg [args];
    
    if (!args)
        return name[3];
    switch (args[1]) {
        case 'type:
            return name[1];
        case 'noarticle:
            return name[2];
        default:
            return name;
    }
.

public match_name_aliases
    arg str;
    
    return (| str in name_aliases |);
.

public namef
    arg [args];
    
    if (!args)
        args = [['name]];
    if (args[1] == 'ref) {
        if (.has_ancestor($named))
            args = [['name], " (", ['objname], ")"];
        else
            args = [['objname]];
    } else if (args[1] == 'xref) {
        if (.has_ancestor($named))
            args = [['objname], " (", ['name], ")"];
        else
            args = [['objname]];
    }
    return pass(@args);
.

public hname
    arg [args];
    
    return "<a href=\"/bin/describe?" + .objname() + "\">" + .namef(@args) + "</a>";
.

public match_name_exact
    arg str;
    
    return $string.match_begin(.name('noarticle), str);
.

root uninit_named
    clear_var('name);
    clear_var('name_aliases);
.

public set_name_aliases
    arg [aliases];
    
    (> .perms(sender()) <);
    name_aliases = aliases;
.



parent $foundation
parent $named
object $gender

var $root inited 1
var $root objname 'gender
var $root created_on 796268969
var $root indestructable 1
var $root flags ['parameters, 'methods, 'code, 'core]
var $gender pronouns 0
var $gender gender 0
var $gender cgender_name 0
var $gender gender_name 0
var $gender person 0
var $gender has 0
var $gender number 0
var $gender context []
var $named name ['normal, "Gendered Object", "a Gendered Object"]
var $named name_aliases []
var $has_settings defined_settings #[]
var $has_settings local_settings #[]
var $has_settings settings #[]

root init_gender
    cgender_name = "";
    gender_name = "";
    
    // these should be inited by hand, later.
    pronouns = #[['pr, "itself"], ['pp, "its"], ['po, "it"], ['ps, "it"], ['pq, "its"], ['prc, "Itself"], ['ppc, "Its"], ['poc, "It"], ['psc, "It"], ['pqc, "Its"], ['have, "has"]];
.

public pronoun
    arg pronoun;
    
    return pronouns[pronoun];
.

public gender
    return gender;
.

public set_gender_names
    arg name, cname;
    
    .perms(sender());
    cgender_name = cname;
    gender_name = name;
.

public set_pronouns
    arg nmbr, ps, po, pp, pq, pr, psc, poc, ppc, pqc, prc;
    var x;
    
    .perms(sender(), 'manager);
    pronouns = #[['pr, pr], ['pp, pp], ['po, po], ['ps, ps], ['pq, pq], ['prc, prc], ['ppc, ppc], ['poc, poc], ['psc, psc], ['pqc, pqc]];
    number = nmbr;
    context = [ps, po, pp, pq, pr, psc, poc, ppc, pqc, prc];
.

public pronouns
    return pronouns;
.

public gender_name
    arg [caps];
    
    caps = [@caps, 'null][1];
    switch (caps) {
        case 'caps:
            return cgender_name;
        default:
            return gender_name;
    }
    
.

public context
    return context;
.



parent $core
object $libraries

var $root child_index 8
var $root fertile 1
var $root inited 1
var $root objname 'libraries
var $root created_on 796268969
var $root indestructable 1
var $root flags ['parameters, 'methods, 'code, 'fertile, 'core]



parent $core
object $misc

var $root child_index 26
var $root fertile 1
var $root inited 1
var $root objname 'misc
var $root created_on 796268969
var $root indestructable 1
var $root flags ['parameters, 'methods, 'code, 'fertile, 'core]



parent $core
object $utilities

var $root child_index 11
var $root inited 1
var $root objname 'utilities
var $root created_on 796268969
var $root indestructable 1
var $root flags ['parameters, 'methods, 'code, 'core]



parent $core
object $frob

var $root child_index 4
var $root fertile 1
var $root inited 1
var $root objname 'frob
var $root created_on 796268969
var $root indestructable 1
var $root flags ['parameters, 'methods, 'code, 'fertile, 'core]

public unparse
    arg rep;
    
    return "<" + $data_lib.unparse(this()) + ", " + $data_lib.unparse(rep) + ">";
.

public new
    arg value;
    
    return (<this(), value>);
.

public to_frob
    arg value;
    
    // this differs from .new in it's application
    return (<this(), value>);
.

public value
    arg value;
    
    return value;
.



parent $core
object $sys

var $sys admins []
var $sys agents [$root, $daemon, $backdoor]
var $sys startup #[['time, 813382395], ['objects, [$login_daemon, $backdoor, $lag_watcher, $http_daemon]], ['heartbeat_interval, 5]]
var $sys starting #[['quota, 75000], ['exit_source, $void], ['place, $the_pit], ['new_user_class, $admin], ['anonymous_user_class, $guest]]
var $sys server_address ["127.0.0.1", "somewhere.cold.org"]
var $sys system_email_addresses #[['default, "nobody@localhost"]]
var $sys core_version "2.0"
var $sys validate_email_addresses 1
var $sys backup #[['interval, 3600], ['last, 813386253], ['next, 813389853]]
var $sys system [$sys, $root, $backdoor, $control]
var $sys touched 813386609
var $sys loggers [$http_interface, $daemon, $user, $connection]
var $sys core_date 0
var $sys names ["root", "foundation", "core", "event_handler", "has_commands", "environment", "has_messages", "public", "network", "help_root", "text", "messaged", "has_settings", "named", "gender", "libraries", "misc", "utilities", "frob", "sys"]
var $sys dumped [$root, $foundation, $core, $event_handler, $has_commands, $environment, $has_messages, $public, $network, $help_root, $text, $messaged, $has_settings, $named, $gender, $libraries, $misc, $utilities, $frob, $sys]
var $root created_on 796268969
var $root inited 1
var $root objname 'sys
var $root indestructable 1
var $root flags ['parameters, 'methods, 'code, 'core]
var $root quota_exempt -1

public next_dbref
    return next_dbref();
.

driver startup
    arg args;
    var opt, str, obj;
    
    if (args && "-clean" in args) {
        catch any {
            (> .clean_database() <);
        } with handler {
            .log($parse_lib.traceback(traceback()));
        }
        shutdown();
        return;
    }
    catch any {
        // Bind all initial functions
        ._bind_functions();
    
        // Set up five-second heartbeat.
        set_heartbeat(startup['heartbeat_interval]);
    
        // set the startup time, reset backup time.
        startup = startup.add('time, time());
        backup = backup.add('next, time() + backup['interval]);
    
        // tell objects who should know, that we are up.
        if (type(args) != 'list)
            args = [];
        for obj in (startup['objects]) {
            .log("Calling " + obj.objname() + ".startup()");
            catch any {
                (> obj.startup(@args) <);
            } with handler {
                .log($parse_lib.traceback(traceback()));
            }
        }
    } with handler {
        .log("Startup ERROR at " + ctime() + ":");
        .log($parse_lib.traceback(traceback(), -1, ""));
    }
.

public server_info
    arg what, [long];
    var tmp;
    
    switch (what) {
        case 'up_time:
            return time() - startup['time];
        case 'startup_time:
            return startup['time];
        case 'server_hostname:
            return server_address[2];
        case 'server_ip:
            return server_address[1];
        case 'load:
            return .load();
        case 'last_backup:
            return backup['last];
        case 'driver_version:
            tmp = version();
            return (long ? "ColdX Genesis " | "") + tostr(tmp[1]) + "." + tostr(tmp[2]) + "-" + tostr(tmp[3]);
        case 'core_version:
            return (long ? "ColdCore " | "") + core_version;
        default:
            throw(~unknown, "Unknown flag.");
    }
.

public callers
    (> .perms(sender(), 'system) <);
    return (> callers() <);
.

public create_user
    arg name, password, email, [type];
    var user;
    
    if (!(| .perms(caller(), $login_interface) |) && !(| .perms(sender(), 'system) |))
        throw(~perm, "Caller and Sender are not allowed to call this method.");
    type = [@type, 'new_user_class][1];
    catch any {
        user = starting[type].spawn(name);
        user.set_name(name);
        if (type == 'new_user_class)
            user.set_password(password);
        else
            user.set_password(tostr(time() / mtime()).crypt());
        user.change_manager(user);
        user.set_data('email, email, 0);
        user.chown(user);
    } with handler {
        // Failed to initialize the child; destroy it.
        if (!(| user.destroy() |)) {
            (| user.uninitialize() |);
            (| del_objname(user.objname('symbol)) |);
            (| del_objname(tosym(name)) |);
            (| destroy(user) |);
        }
        rethrow(error());
    }
    return user;
.

public admins
    return admins;
.

public is_admin
    arg obj;
    
    return obj == $sys || obj in admins;
.

public binary_dump
    if (!$sys.is_admin(sender()))
        throw(~perm, "Sender is not an admin.");
    pause();
    return backup();
.

public shutdown
    var opt, str, obj;
    
    (> .perms(sender(), $sys) <);
    
    // tell startup objects that we are closing shop
    for obj in (startup['objects]) {
        .log("Calling " + obj.objname() + ".shutdown()");
        catch any {
            (> obj.shutdown() <);
        } with handler {
            .log($parse_lib.traceback(traceback()));
        }
    }
    return shutdown();
.

public change_sender_parents
    arg parents;
    var p;
    
    if (caller() != $root)
        throw(~perm, "Caller is not $root.");
    (> chparents(sender(), parents) <);
.

public spawn_sender
    arg suffix, manager, [owners];
    var namestr;
    
    (> .perms(caller(), $root, $sys) <);
    if (!owners)
        owners = [manager];
    namestr = tostr(sender().objname('symbol)) + "_" + suffix;
    return .create([sender()], tosym(namestr), manager, owners);
.

public destroy_sender
    (> .perms(caller(), $root) <);
    (| .deassign_objname(sender().objname('symbol)) |);
    (> destroy(sender()) <);
.

public is_system
    arg obj;
    
    return obj in system || obj.has_ancestor($backdoor);
.

public log
    arg text;
    var l;
    
    if (!sender() in loggers) {
        if (!(| .perms(sender(), 'system) |) && !(| .perms(caller(), 'system) |))
            throw(~perm, "Only system objects can log.");
    }
    if (type(text) == 'list) {
        for l in (text)
            .log(l);
    } else {
        log("[" + $time.format("%d %h %y %H:%M") + "] " + text);
    }
.

public open_connection
    arg [args];
    
    (> .perms(caller(), $network) <);
    (> open_connection(@args) <);
.

driver heartbeat
    if (sender() != 0)
        throw(~perm, "Sender is not the server.");
    (| $scheduler.pulse() |);
    if (time() > backup['next])
        .do_backup(this(), 'interval);
.

public do_backup
    arg [args];
    var line, who, how, name, time, dirty;
    
    (> .perms(sender(), 'system) <);
    dirty = .dirty();
    who = [@args, sender()][1];
    how = [@args, 'request, 'request][2];
    if (!valid(who))
        who = sender();
    backup = backup.add('next, time() + backup['interval]);
    backup = backup.add('last, time());
    if (how == 'interval && !dirty)
        return;
    catch any {
        name = who.namef('ref);
        .log("BACKUP (" + name + ") ");
        line = "Backup called at " + $time.ltime() + " by " + name;
        $channels.announce('System, line);
    }
    pause();
    time = time();
    .binary_dump();
    catch any {
        line = "Backup completed, executing filesystem cleanup...";
        $channels.announce('System, line);
        pause();
    }
    .execute("backup", []);
    time = time() - time;
    catch any {
        line = "Backup completed, elapsed time ";
        $channels.announce('System, line + $time.elapsed(time, 'long));
    }
.

public set_startup
    arg what, value;
    var valid;
    
    (> .perms(sender(), 'system) <);
    valid = startup.keys();
    if (!what in valid)
        throw(~type, "Key must be one of " + toliteral(valid));
    startup = startup.add(what, value);
.

public sender_data
    var output, i;
    
    if (caller() != $root)
        throw(~perm, "Caller is not $root.");
    return data(sender());
.

public get_system_email
    arg what;
    var email;
    
    // email directory for system services, such as arch admins.
    email = (| system_email_addresses[what] |);
    if (!email)
        email = (| system_email_addresses['default] |) || "<no email set>";
    return email;
.

public new_admin
    if (caller() != $admin)
        throw(~perm, "Caller is not $admin.");
    admins = admins.setadd(sender());
.

public agents
    return agents;
.

public text_dump
    .perms(sender(), 'this);
    pause();
    return text_dump();
.

public get_startup
    arg what;
    
    return starting[what];
.

public set_starting
    arg what, value;
    var valid;
    
    (> .perms(sender(), 'system) <);
    valid = starting.keys();
    if (!what in valid)
        throw(~type, "Key must be one of " + toliteral(valid));
    starting = starting.add(what, value);
.

public execute
    arg script, args, [background];
    
    (> .perms(sender(), 'system) <);
    (> execute(script, args, @background) <);
.

public get_starting
    arg what;
    
    return starting[what];
.

public create
    arg parents, name, manager, [owners];
    var new;
    
    .perms(sender(), 'system);
    new = create(parents);
    catch any {
        new.set_objname(name);
        new.initialize();
        new.change_manager(manager);
        new.chown(@[@owners, [new]][1]);
    } with handler {
        // Failed to initialize the child; destroy it.
        if (!(| new.destroy() |)) {
            (| new.uninitialize() |);
            (| del_objname(new.objname('symbol)) |);
            (| del_objname(tosym(name)) |);
            (| destroy(new) |);
        }
        rethrow(error());
    }
    return new;
.

public system
    return system;
.

public bind_port
    arg port, obj;
    
    (> .perms(caller(), $network, $backdoor) <);
    (> bind_port(port, obj) <);
.

public del_system_email
    arg key;
    
    (> .perms(sender(), 'manager) <);
    system_email_addresses = system_email_addresses.del(key);
.

public add_system_email
    arg key, email;
    
    (> .perms(sender(), 'manager) <);
    if (type(key) != 'symbol)
        throw(~type, "Key is not a symbol.");
    if (type(email) != 'string)
        throw(~type, "Email address is not a string.");
    system_email_addresses = system_email_addresses.add(key, email);
.

public compile
    arg code, name;
    var line;
    
    (> .perms(sender()) <);
    line = "SYSTEM: ." + tostr(name) + "() MODIFIED";
    line = line + " by " + sender().namef('ref);
    .log(line);
    return (> pass(code, name) <);
.

public unbind_port
    arg port, obj;
    
    (> .perms(caller(), $network, $backdoor) <);
    (> unbind_port(port) <);
.

public tasks
    return (> tasks() <);
.

public resume
    arg task, [args];
    
    (> resume(task, @args) <);
.

public suspend
    return (> suspend() <);
.

public validate_email_addresses
    return validate_email_addresses;
.

public reassign_connection
    arg obj;
    
    (> .perms(caller(), $network, $backdoor) <);
    (> reassign_connection(obj) <);
.

public cancel
    arg [args];
    
    return (> cancel(@args) <);
.

public signal
    arg sig, sigstr;
    var line;
    
    if (sender() || caller())
        throw(~perm, "Sender is not the server.");
    line = "** Caught Signal: " + sigstr + " **";
    (| $channels.announce('System, line) |);
    if (!(sig in [1, 13, 16, 30, 31])) {
        (| $channels.announce('System, "******************************") |);
        (| $channels.announce('System, "** IMMINENT SERVER SHUTDOWN **") |);
        (| $channels.announce('System, "******************************") |);
    }
.

root sender_going_away
    admins = admins.setremove(sender());
    agents = agents.setremove(sender());
    system = system.setremove(sender());
.

public do_loadcheck
    return;
    (> .perms(sender(), 'system) <);
    load = load.add('load, load());
    (| $channels.announce('System, $time.ltime() + " LOADCHECK: " + load['load]) |);
.

public status
    return (> status() <) + [backup['interval], backup['last], backup['next]];
.

public hostname
    arg [args];
    
    return (> hostname(@args) <);
.

private _loop_for_core
    arg code;
    var obj;
    
    $root.compile(code, '___coretmp);
    for obj in ($root.descendants()) {
        obj.___coretmp();
        pause();
    }
    $root.del_method('___coretmp);
.

private initialize_core
    var obj;
    
    (| .clean_database() |);
    
    // reset child indices
    ._loop_for_core(["child_index = 0;"]);
.

public clean_database
    var obj, objs, iobjs, count, name, def;
    
    (> .perms(caller(), definer()) <);
    .log("Cleaning database ...");
    count = $root._clean_database();
    .log("-- " + tostr(count[1] + count[2]) + " total objects --");
    .log("   (" + tostr(count[1]) + " invalid descendants of $root)");
    .log("   (" + tostr(count[3]) + " with clean methods in the wrong location)");
    objs = $place.descendants();
    for obj in (objs) {
        if (!obj.has_flag('core))
            obj.rehash_caches();
    
        // incase we need more ticks
        pause();
    }
    .log("-- Places command cache rehash pass complete --");
    .log("   (" + tostr(objs.length()) + " total places)");
    
    // rehash user info
    objs = $user.descendants();
    for obj in (objs) {
        if (!obj.has_flag('core))
            obj.purge_caches();
        if ((| obj.home() != obj.location() |))
            obj.move_to(obj.home());
    
        // incase we need more ticks
        pause();
    }
    .log("-- User command cache reset pass complete --");
    .log("   (" + tostr(objs.length()) + " total users)");
    
    // validate all locations content's
    objs = $location.descendants();
    for obj in (objs) {
        // incase we need more ticks
        pause();
        (| obj.validate_contents() |);
        if (obj.is($located_location)) {
            if (!valid(obj.location()) || !(obj in obj.location().contents())) {
                obj.move_to(obj.home() || $lost_and_found);
                icount = icount + 1;
            }
        }
    }
    .log("-- Location pass complete --");
    .log("   (" + tostr(objs.length()) + " objects with invalid locations)");
    for obj in ($connection_interface.children())
        (| obj.rehash_caches() |);
.

public ip
    arg [args];
    
    return (> ip(@args) <);
.

public assign_objname
    arg name;
    var keepers, x;
    
    (> .perms(caller(), $root, $sys) <);
    if (type(name) != 'symbol)
        throw(~type, "Name must be given as a symbol.");
    
    // don't run this for now--tosym is fucked.
    if (0) {
        // yeah, we change it to a string, but they don't have to know that.
        name = tostr(name);
    
        // lowercase all names:
        name = name.lowercase();
    
        // If it isn't a keeper toss the good old error
        keepers = "abcdefghijklmnopqrstuvwxyz1234567890_";
        for x in [1 .. name.length()] {
            if (!(name[x] in keepers))
                throw(~type, "Name has one or more non-alphanumeric characters.");
        }
        name = tosym(name);
    }
    
    // make sure nobody has it yet
    if ((| get_dbref(name) |) != ~namenf)
        throw(~perm, "Name already assigned to " + get_dbref(name).namef('ref));
    
    // woo woo, i'm filled with joy, lets give them the name.
    add_objname(name, sender());
.

public deassign_objname
    arg name;
    
    (> .perms(caller(), $root, $sys) <);
    del_objname(name);
.

private _bind_functions
    // these will eventually be native methods
    bind_function('buffer_len, $buffer);
    bind_function('buffer_retrieve, $buffer);
    bind_function('buffer_append, $buffer);
    bind_function('buffer_replace, $buffer);
    bind_function('buffer_add, $buffer);
    bind_function('buffer_truncate, $buffer);
    bind_function('buffer_tail, $buffer);
    bind_function('buffer_to_string, $buffer);
    bind_function('buffer_to_strings, $buffer);
    bind_function('buffer_from_string, $buffer);
    bind_function('buffer_from_strings, $buffer);
    bind_function('dict_keys, $dictionary);
    bind_function('dict_add, $dictionary);
    bind_function('dict_del, $dictionary);
    bind_function('dict_contains, $dictionary);
    bind_function('listlen, $list);
    bind_function('sublist, $list);
    bind_function('insert, $list);
    bind_function('replace, $list);
    bind_function('delete, $list);
    bind_function('setadd, $list);
    bind_function('setremove, $list);
    bind_function('union, $list);
    bind_function('strlen, $string);
    bind_function('substr, $string);
    bind_function('explode, $string);
    bind_function('pad, $string);
    bind_function('match_begin, $string);
    bind_function('match_template, $string);
    bind_function('match_pattern, $string);
    bind_function('match_regexp, $string);
    bind_function('strsub, $string);
    bind_function('crypt, $string);
    bind_function('uppercase, $string);
    bind_function('lowercase, $string);
    bind_function('strcmp, $string);
    bind_function('strfmt, $string);
    bind_function('next_dbref, $sys);
    bind_function('load, $sys);
    bind_function('status, $sys);
    bind_function('version, $sys);
    bind_function('strftime, $time);
    
    //
    bind_function('bind_function, this());
    bind_function('unbind_function, this());
    bind_function('create, this());
    bind_function('chparents, this());
    bind_function('log, this());
    bind_function('reassign_connection, $daemon);
    bind_function('binary_dump, this());
    bind_function('text_dump, this());
    bind_function('execute, this());
    bind_function('shutdown, this());
    bind_function('bind_port, $daemon);
    bind_function('unbind_port, $daemon);
    bind_function('hostname, $network);
    bind_function('ip, $network);
    bind_function('load, this());
    bind_function('status, this());
    bind_function('backup, this());
.

public add_to_system
    arg obj;
    
    if (!.is_admin(sender()))
        throw(~perm, "Sender is not an admin.");
    if (!(obj in admins || obj in agents))
        throw(~perm, "Object is not an agent or admin.");
    system = system.union([obj]);
.

public del_from_system
    arg obj;
    
    if (!.is_admin(sender()))
        throw(~perm, "Sender is not an admin.");
    if (!(obj in admins || obj in agents))
        throw(~perm, "Object is not an agent or admin.");
    system = system.setremove(obj);
.

public touch
    touched = time();
.

public touched
    return touched;
.

public backup
    return backup;
.

public dirty
    return touched > backup['last];
.

public _clean_object_ownership
    arg objs;
    var code, cname, obj;
    
    (> .perms(caller(), definer()) <);
    
    // this will be removed when object ownership works better
    code = ["var obj;"];
    code = code + ["for obj in (owners) {"];
    code = code + ["    if (!(this() in obj.owned()))"];
    code = code + ["         owners = owners.setremove(obj);"];
    code = code + ["    pause();"];
    code = code + ["}"];
    code = code + ["for obj in (owned) {"];
    code = code + ["    if (!valid(obj)) owned = owned.setremove(obj);"];
    code = code + ["    pause();"];
    code = code + ["}"];
    code = code + ["if (!valid(manager)) manager = this();"];
    cname = tosym("___clean_" + tostr(time()));
    
    // do it
    catch any {
        $root.compile(code, cname);
        for obj in (objs) {
            (| obj.(cname)() |);
            pause();
        }
    }
    $root.del_method(cname);
    .log("-- Object Ownership pass completed --");
.

public do_shutdown
    arg [args];
    var why, time, increments, line, name, mins;
    
    if (!$sys.is_admin(sender()) || definer() != this())
        throw(~perm, "Sender is not an admin.");
    time = [@args, 0][1];
    why = [@args, "", ""][2];
    increments = [600, 300, 180, 60];
    while (increments && time < increments[1])
        increments = increments.delete(1);
    name = sender().namef('xref);
    .log("*** SHUTDOWN called by " + name + " ***");
    if (why) {
        why = "*** " + why + " ***";
        .log(why);
    }
    while (1) {
        if (!increments) {
            $channels.announce('all, "*** SYSTEM SHUTDOWN ***");
            break;
        }
        line = "*** SYSTEM SHUTDOWN IN ";
        mins = increments[1] / 60;
        line = line + tostr(mins) + " MINUTE" + (mins == 1 ? "" | "S");
        line = line + " CALLED BY " + name + " ***";
        $channels.announce('all, line);
        if (why)
            $channels.announce('all, why);
        $scheduler.sleep(increments[1]);
        increments = increments.delete(1);
    }
    return .shutdown();
.

public core_date
    return core_date;
.

public loggers
    return loggers;
.

private dump_names
    var x, o, n;
    
    x = 1;
    o = [];
    if ((| names |) == ~paramnf)
        return "No names to dump";
    for n in (names) {
        pause();
        if (n == "sys") {
            o = o + ["name " + n + " 0"];
        } else {
            o = o + ["name " + n + " " + tostr(x)];
            x = x + 1;
        }
    }
    .tell(o);
    .del_parameter('names);
.

private dump_db
    arg c;
    var obj, list;
    
    // This uses a dumping script i've written in perl.  Once file
    // ops are fully online, this will be much more comprehensive.
    // Furthermore, you need to dump the names by hand!
    // Also, this is _NOT_ the 1.0 textdump format.
    if ((| names |) == ~paramnf)
        .add_parameter('names);
    if ((| dumped |) == ~paramnf)
        .add_parameter('dumped);
    names = dumped = [];
    list = $root.descendants();
    c.write("#$#LOG#$#");
    while (list) {
        obj = (| list[1] |);
        if (!obj)
            break;
        list = list.delete(1);
        pause();
        while (list && obj && obj in dumped) {
            obj = (| list[1] |);
            if (!obj)
                break;
            list = list.delete(1);
            pause();
        }
        if (obj)
            .dump_object(obj, c);
    }
    c.write("#$#/LOG#$#");
    .del_parameter('dumped);
    return "Now log $sys.dump_names()";
.

private dump_object
    arg obj, c;
    var p, name, v, l, code, line, f, flags;
    
    // call .dump_db()
    for p in (obj.parents()) {
        if (!(p in dumped))
            .dump_object(p, c);
    }
    dumped = dumped + [obj];
    names = names + [tostr(obj.objname('symbol))];
    c.write(["", ""]);
    for p in (obj.parents())
        c.write("parent " + p.objname());
    c.write(["object " + obj.objname(), ""]);
    for p in (obj.data()) {
        name = (| p[1].objname() |) || toliteral(p[1]);
        for v in (p[2])
            c.write("var " + name + " " + tostr(v[1]) + " " + $data_lib.unparse(v[2]));
        pause();
    }
    c.write("");
    for v in (obj.methods()) {
        if (v == 'tmp_eval)
            continue;
        switch (obj.method_state(v)) {
            case 'root:
                line = "root ";
            case 'driver:
                line = "driver ";
            case 'public:
                line = "public ";
            case 'protected:
                line = "protected ";
            case 'private:
                line = "private ";
        }
        code = obj.list_method(v);
        line = line + tostr(v);
    
        // write $symbol and just have flags.map('tostr).join(", ");
        flags = obj.method_flags(v);
        f = "";
        if ('disallow_overrides in flags)
            f = "disallow_overrides";
        if ('synchronized in flags)
            f = (f ? f + ", " | "") + "synchronized";
        if ('locked in flags)
            f = (f ? f + ", " | "") + "locked";
        if ('native in flags)
            f = (f ? f + ", " | "") + "native";
        c.write(line + (f ? ": " + f | ""));
        for l in (code)
            c.write("    " + l);
        c.write([".", ""]);
        pause();
    }
.



parent $core
object $control

var $root objname 'control
var $root managed [$foundation, $core, $event_handler, $has_commands, $environment, $public, $help_root, $text, $messaged, $gender, $libraries, $misc, $utilities, $frob, $sys, $backdoor, $control, $physical, $described, $exit, $thing, $place, $help_ui, $help_index, $help, $help_node, $note, $mail_message, $event_frob, $named, $user_data, $gender_female, $gender_male, $gender_plural, $gender_neuter, $dictionary, $time_root, $buffer, $string, $command_lib, $http_lib, $object_lib, $integer, $parse_lib, $code_lib, $list, $security, $mail_lib, $time_lib, $data_lib, $mail_root, $db, $user_parsers, $filters, $lock_parser, $world, $housekeeper, $root_evaluator, $channels, $places, $motd, $heart, $logic_frob, $realms_frob, $thing_frob, $read_parser, $located, $location, $user_interfaces, $editors, $located_location, $nothing, $nowhere, $void, $help_summary, $log, $movement_event, $gendered, $mail_list, $time, $dark_time, $mail_db, $registry, $null_parser, $command_aliases_parser, $conference_parser, $epic_filter, $wrap_filter, $xor, $and, $lock_frob, $not, $or, $true, $false, $realm_of_creation, $wearable_frob, $interaction, $mail_ui, $command_aliases, $bad_commands, $settings_ui, $tree_ui, $programmer_interface, $help_interface_new, $set_ui, $generic_editor, $body, $http_log, $reaper_log, $login_log, $mail_list_news, $place_db, $user_db, $or_lock_frob, $true_lock_frob, $false_lock_frob, $not_lock_frob, $object_lock_frob, $and_lock_frob, $inside_lock_frob, $code_editor, $note_editor, $scheduler, $ctext_lib, $help_lib, $symbol, $cml2, $body_cave, $detailed_place, $the_pit, $trie, $lag_watcher, $editor, $has_messages, $has_settings, $tree, $has_hooks, $ctext_frob, $ctext_tag, $reaper, $ctext2_frob, $message_frob, $ctext_filter, $cml2_evaluator, $cml2_form, $cml2_compiler, $ctext_generator, $ctext_format, $messages_ui, $cml2_format, $cml2_base_eval, $cml2_uncompiler, $connection, $daemon, $connection_interface, $login_connection, $http_connection, $http_daemon, $login_daemon, $login_interface, $http_interface, $network, $help_index_root, $projects, $project, $command_cache, $mail_envelope, $mail_address, $command_parser, $help_editing_ui, $on_location, $in_location, $mail_list_bugs, $storyteller, $lost_and_found, $cml2_telnet_format, $player]
var $root created_on 812170946
var $root inited 1
var $root flags ['parameters, 'methods, 'code, 'core]



parent $core
object $backdoor

var $root child_index 64
var $root inited 1
var $root objname 'backdoor
var $root created_on 796268969
var $root indestructable 1
var $root flags ['core]
var $root quota_exempt -1
var $backdoor buffer `[]
var $backdoor ip ""
var $backdoor hostname ""
var $backdoor line_buffer []
var $backdoor valid_hosts ["127.0.0.1"]
var $backdoor passwords #[]
var $backdoor connected 1
var $backdoor programming #[]
var $backdoor port 1169
var $backdoor who ""

root init_backdoor
    buffer = `[];
    ip = "";
    hostname = "";
    line_buffer = [];
    programming = #[];
    who = "";
.

root uninit_backdoor
    buffer = `[];
    ip = "";
    hostname = "";
    line_buffer = [];
    programming = [];
.

driver parse
    arg incoming;
    var lines, line, index;
    
    lines = buffer.append(incoming).to_strings();
    index = lines.length();
    buffer = lines[index];
    lines = lines.delete(index);
    line_buffer = [@line_buffer, @lines];
    while (line_buffer) {
        line = line_buffer[1];
        line_buffer = line_buffer.delete(1);
        (| .parse_line(line) |);
    }
.

protected parse_line
    arg line;
    var l;
    
    if (sender() != this())
        throw(~perm, "Sender is not this.");
    catch any {
        if (connected) {
            if (programming)
                ._handle_programming_line(line);
            else
                ._handle_command_line(line);
        } else {
            ._handle_login(line);
        }
    } with handler {
        for l in ($parse_lib.traceback(traceback()))
            .echo(l);
    }
.

protected quit_cmd
    arg [cmd];
    
    if (sender() != this())
        throw(~perm, "Sender is not this.");
    .echo("Goodbye.");
    .backdoor_log("Disconnect <" + .address() + ">");
    .close();
.

protected eval_cmd
    arg cmd, [args];
    var code, result;
    
    if (sender() != this())
        throw(~perm, "Sender is not this.");
    code = args.to_string() + ";";
    if (code && code[1] == ";")
        code = code.subrange(2);
    else
        code = "return " + code;
    result = .eval([code], this());
    if (result[1] == 'errors)
        .echo(result[2]);
    else
        .echo("=> " + .toliteral(result[2]));
.

protected eval_as_cmd
    arg cmd, [args];
    var result, code, obj;
    
    if (sender() != this())
        throw(~perm, "Sender is not this.");
    args = args.to_string();
    result = $string.match_template("* eval *", args);
    if (!result) {
        .echo("Syntax: `as <object> eval <code>`");
        return;
    }
    obj = (| $object_lib.to_dbref(result[1]) |);
    if (!obj) {
        .echo("Unable to find object \"" + result[1] + "\".");
        return;
    }
    code = result[3] + ";";
    result = obj.eval([code]);
    if (result[1] == 'errors)
        .echo(result[2]);
    else
        .echo("as eval => " + toliteral(result[2]));
.

protected echo
    arg what;
    var line;
    
    if (type(what) == 'list) {
        for line in (what)
            .echo(line);
    } else {
        ._echo(what);
    }
.

protected eval_as_to_cmd
    arg cmd, [args];
    var result, code, obj, target;
    
    if (sender() != this())
        throw(~perm, "Sender is not this.");
    args = args.to_string();
    result = $string.match_template("* as * eval *", args);
    if (!result) {
        .echo("Syntax: `definer <object> as <object> eval <code>`");
        return;
    }
    obj = (| $object_lib.to_dbref(result[1]) |);
    target = (| $object_lib.to_dbref(result[3]) |);
    if (!obj) {
        .echo("Unable to find object \"" + result[1] + "\".");
        return;
    }
    if (!target) {
        .echo("Unable to find object \"" + result[3] + "\".");
        return;
    }
    code = result[5] + ";";
    result = obj.eval([code], target);
    if (result[1] == 'errors)
        .echo(result[2]);
    else
        .echo("definer as eval => " + toliteral(result[2]));
.

protected close
    close_connection();
    .destroy();
.

protected set_password
    arg what, word;
    var x, i, a, cword;
    
    if (!$sys.is_admin(sender())) {
        .log_backdoor("Invalid set password attempt by " + sender().namef('xref));
        throw(~perm, "Permission denied, only admins may set backdoor passwords.");
    }
    if (word.length() < 6)
        throw(~badpasswd, "Passwords must be at least 6 characters long.");
    for x in [1 .. word.length()] {
        if (word[x] in "1234567890")
            i = i + 1;
        else
            a = a + 1;
    }
    if (a < 2 || i < 2)
        throw(~badpasswd, "Passwords must contain at least 2 numeric characters and 2 non-numeric characters.");
    cword = word.crypt();
    if (cword in passwords.values())
        throw(~badpasswd, "That password already exists.");
    passwords = passwords.add(what, cword);
.

protected del_password
    arg what;
    
    if (!$sys.is_admin(sender())) {
        .log_backdoor("Invalid password removal attempt by " + sender().namef('xref));
        throw(~perm, "Permission denied, only admins may adjust backdoor passwords.");
    }
    passwords = passwords.add(what);
.

protected address
    arg [args];
    
    if (sender() != this())
        throw(~perm, "Sender is not this.");
    args = [@args, 'hostname][1];
    switch (args) {
        case 'ip:
            return ip;
        case 'hostname:
            return hostname;
    }
.

protected set_address
    arg host;
    
    if (sender() != this() && !sender().has_ancestor(definer()))
        throw(~perm, "Sender is not this.");
    ip = host;
    hostname = hostname(ip);
.

driver connect
    arg host, socket;
    var c;
    
    if (!(host in valid_hosts)) {
        .backdoor_log("Connection from invalid host: " + host);
        .echo("Your host is not one of the valid hosts.");
        .close();
        return;
    }
    .backdoor_log("Connection from: " + host);
    c = .spawn();
    if ($daemon.reassign_connection(c))
        c.set_address(host);
    else
        c.destroy();
.

protected program
    if (sender() != this())
        throw(~perm, "Sender is not this.");
    programming = #[['task_id, task_id()], ['code, []]];
    return $sys.suspend();
.

protected check_passwords
    arg str;
    var line, pwd;
    
    if (sender() != this())
        throw(~perm, "Sender is not this.");
    line = str.explode();
    pwd = $backdoor.passwords();
    if (!(| pwd[line[1]] |) || line.length() != 4 || !.check_encrypted(line[1], line[2]) || !.check_encrypted("back1", line[3]) || !.check_encrypted("back2", line[4]))
        return 0;
    return 1;
.

protected disconnect
    arg [args];
    
    if (sender() || caller())
        throw(~perm, "Sender is not the server.");
    .close();
.

public startup
    arg [args];
    var opt, curr_port;
    
    (> .perms(caller(), $sys) <);
    catch any {
        opt = "-pbackdoor" in args;
        curr_port = (| toint(args[opt + 1]) |) || port;
        $sys.log("** Starting $backdoor on port " + tostr(curr_port) + " **");
        $daemon.bind_port(curr_port);
    } with handler {
        $sys.log($parse_lib.traceback(traceback()));
    }
.

public passwords
    if (!sender().has_ancestor(this()))
        throw(~perm, "Sender is not descended from this.");
    return passwords;
.

protected backdoor_log
    arg str;
    
    if (sender() != this())
        throw(~perm, "Sender is not this.");
    $sys.log(.objname() + " (" + who + "): " + str);
.

protected check_encrypted
    arg key, str;
    var pwd;
    
    pwd = ($backdoor.passwords())[key];
    if (sender() != this())
        throw(~perm, "Sender is not this.");
    return str.crypt(pwd.subrange(1, 2)) == pwd;
.

protected _echo
    arg what;
    
    // send off a string or buffer
    if (sender() != this())
        throw(~perm, "Sender is not this.");
    (> echo($buffer.from_strings([what])) <);
.

protected program_cmd
    arg cmd, what;
    var code, ref, ignore, errors;
    
    if (sender() != this())
        throw(~perm, "Sender is not this.");
    ref = $parse_lib.full_reference(what);
    if (ref[1] != 'method || !ref[3]) {
        .echo("Invalid object.method() reference, ignoring code until \".\"");
        ignore = 1;
    }
    .echo("-- Enter text for " + ref[2].objname() + "." + tostr(ref[3]) + " --");
    code = .program();
    if (code == 'aborted) {
        .echo("** Aborted **");
    } else if (ignore) {
        .echo("Finished ignoring text.");
    } else {
        errors = ref[2].compile(code, ref[3]);
        if (errors)
            .echo(errors);
        else
            .echo("Method compiled.");
    }
.

protected _handle_programming_line
    arg line;
    
    if (sender() != this())
        throw(~perm, "Sender is not this.");
    if (!line) {
        programming = programming.add_elem('code, "");
    } else if (line == ".") {
        $sys.resume(programming['task_id], programming['code]);
        programming = #[];
    } else if (line == "@abort") {
        $sys.cancel(programming['task_id]);
        programming = #[];
        .echo("** Aborted **");
    } else {
        programming = programming.add_elem('code, line);
    }
.

protected _handle_command_line
    arg line;
    var cmds, cmd;
    
    if (sender() != this())
        throw(~perm, "Sender is not this.");
    
    //
    cmds = "list, program, quit, eval, as eval, def as eval";
    if (!line) {
        .echo("Available commands: " + cmds);
        return;
    }
    
    // because I'm lazy, check for ';'
    if (line[1] == ";")
        line = "eval " + line.subrange(2);
    .backdoor_log("Command: " + line);
    cmd = line.explode();
    if (cmd.length() == 1)
        cmd = [cmd[1], ""];
    switch (cmd[1]) {
        case "@program", ".program", "program":
            .program_cmd(@cmd);
        case "@quit", "quit":
            .quit_cmd(@cmd);
        case "eval":
            .eval_cmd(@cmd);
        case "as":
            .eval_as_cmd(@cmd);
        case "definer", "def":
            .eval_as_to_cmd(@cmd);
        case "list", "@list", "@nlist":
            .list_cmd(@cmd);
        default:
            .echo("Available commands: " + cmds);
    }
.

protected _handle_login
    arg line;
    
    if (sender() != this())
        throw(~perm, "Sender is not the definer.");
    if (.check_passwords(line)) {
        connected = 1;
        who = (line.explode())[1];
        .backdoor_log("Valid login sequence.");
        .echo("Valid login sequence.");
    } else {
        .backdoor_log("Invalid login sequence (" + ((| line[1] |) || "<none>") + " ...) from: " + .address());
        .echo("Invalid login sequence.");
        .close();
    }
.

protected list_cmd
    arg cmd, what;
    var code, ref, ancestor, nums, line;
    
    if (sender() != this())
        throw(~perm, "Sender is not this.");
    if (!what)
        return .echo("no reference specified.");
    catch any {
        ref = $parse_lib.full_reference(what);
        nums = cmd == "@nlist";
        if (ref[1] != 'method || !ref[3]) {
            .echo("Invalid object.method() reference.");
            return;
        }
        ancestor = ref[2].find_method(ref[3]);
        code = ancestor.list_method(ref[3]);
        if (nums) {
            for line in [1 .. code.length()]
                code = code.replace(line, (line < 10 ? " " | "") + tostr(line) + ": " + code[line]);
        } else {
            for line in [1 .. code.length()]
                code = code.replace(line, "  " + code[line]);
        }
        .echo("program " + ancestor.objname() + "." + tostr(ref[3]));
        .echo(code);
        .echo(".");
    } with handler {
        .echo(traceback()[1][2]);
    }
.

public linelen
    return 79;
.

protected toliteral
    arg data;
    var str, element, association, pos;
    
    switch (type(data)) {
        case 'integer, 'string, 'symbol, 'error, 'buffer:
            return toliteral(data);
        case 'dbref:
            if (!valid(data))
                return "** invalid object (" + toliteral(data) + ") **";
            return (| data.objname() |) || toliteral(data);
        case 'list:
            if (!data)
                return "[]";
            str = "[";
            for element in (data.subrange(1, data.length() - 1)) {
                str = str + .toliteral(element);
                str = str + ", ";
            }
            str = str + .toliteral(data[data.length()]);
            return str + "]";
        case 'dictionary:
            if (!data)
                return "#[]";
            str = "#[";
            for association in (data) {
                str = str + .toliteral(association);
                str = str + ", ";
            }
            return str.subrange(1, str.length() - 2) + "]";
        case 'frob:
            return toliteral(data);
    }
.



parent $named
parent $has_commands
object $described

var $described prose []
var $root inited 1
var $root objname 'described
var $root created_on 796268969
var $root flags ['parameters, 'methods, 'code, 'core]
var $messaged verbs #[["l?ook at %this", ['look_vrb, 'noremote]], ["l?ook %this", ['look_vrb, 'noremote]], ["exam?ine %this", ['examine_vrb, 'remote]]]
var $named name ['uniq, "Generic Described Object", "the Generic Described Object"]
var $named name_aliases []
var $has_commands shortcuts #[]
var $has_commands remote #[["l?ook", [["l?ook", "at *", "l?ook at <this>", 'look_at_cmd, #[[2, ['this, []]]]], ["l?ook", "*", "l?ook <this>", 'look_at_cmd, #[[1, ['this, []]]]]]]]

root init_described
    prose = [];
.

public description
    arg flags;
    var out, name;
    
    // No option of getting the name, you always do
    name = (<$ctext_format, ["subj", #[], #[], [.name()], 'do_subj]>);
    if (flags['brief])
        name = [name];
    else
        name = [name, (<$ctext_format, ["br", #[], #[], [], 'do_br]>)];
    out = (<$ctext2_frob, [name, #[]]>);
    if (flags['prose])
        out = out.append(.prose());
    return [out];
.

public prose: disallow_overrides
    arg [no_default];
    
    return prose || (no_default ? 0 | "You see nothing special");
.

public set_prose
    arg new;
    
    (> .perms(sender(), 'manager) <);
    switch (type(new)) {
        case 'string, 'list:
            new = (> $cml2_compiler.compile_cml(new) <);
        case 'frob:
            // we'll let this pass by unharmed
        default:
            throw(~invalid, "Prose can be submitted as CML or Ctext");
    }
    prose = new;
.

root uninit_described
    prose = 0;
.

public coreify_described
    (> .perms($root, caller()) <);
    .compile(["prose = " + toliteral(prose) + ";"], 'CORE_EVAL);
.

public look_at_cmd
    arg [args];
    
    return .get_description();
.

public get_description: disallow_overrides
    arg [def_flags];
    var flags;
    
    flags = $code_lib.default_description_flags();
    if (def_flags && type(def_flags[1]) == 'dictionary) {
        flags = def_flags[1].union(flags);
    } else {
        flags = flags.add('actor, sender());
        flags = flags.add_elem('exclude, sender());
    }
    return .description(flags);
.



parent $named
object $gendered

var $root inited 1
var $root objname 'gendered
var $root created_on 796268969
var $root flags ['parameters, 'methods, 'code, 'core]
var $messaged verbs 0
var $gendered gender 0
var $described prose #[]
var $named name ['uniq, "The Generic Gendered Object", "the The Generic Gendered Object"]
var $named name_aliases []
var $has_settings defined_settings #[["gender", #[['get, 'gender], ['set, 'set_gender], ['check, 'check_gender], ['del, 'delete_local_setting], ['check_args, []], ['get_args, []], ['set_args, []]]]]

public gender_context
    return gender.pronoun('po);
.

root init_gendered
    gender = $gender_neuter;
.

public set_gender
    arg definer, name, value, [args];
    
    (> .perms(sender(), 'manager) <);
    gender = value;
.

public gender
    arg [args];
    
    return gender;
.



parent $event_handler
parent $described
parent $gendered
parent $environment
parent $has_settings
object $physical

var $root trusted []
var $root inited 1
var $root objname 'physical
var $root created_on 796268969
var $root flags ['parameters, 'methods, 'code, 'core]
var $messaged verbs #[]
var $described prose []
var $gendered gender $gender_neuter
var $physical visibility 0
var $named name ['uniq, "Generic Physical Object", "the Generic Physical Object"]
var $named name_aliases []
var $has_settings defined_settings #[["visibility", #[['get, 'visibility], ['set, 'set_visibility], ['check, 'is_type], ['del, 'delete_local_setting], ['check_args, ['integer]], ['get_args, []], ['set_args, []]]]]
var $has_settings local_settings #[]
var $has_settings settings #[]

public set_visibility
    arg definer, name, value, [args];
    
    (> .perms(sender()) <);
    visibility = value;
.

public visibility
    arg [args];
    
    return visibility;
.

public is_visible_to
    arg whom;
    
    return .visibility() >= whom.location().darkness();
.

public set_darkness
    arg definer, name, value, [args];
    
    (> .perms(sender()) <);
    darkness = value;
.



parent $has_commands
object $command_cache

var $root objname 'command_cache
var $root child_index 3
var $root fertile 1
var $root created_on 796605573
var $root inited 1
var $root flags ['parameters, 'methods, 'code, 'fertile, 'core]
var $command_cache shortcut_cache 0
var $command_cache remote_cache 0
var $command_cache local_cache 0
var $has_commands shortcuts #[]

root uninit_command_cache
    (| .purge_caches() |);
.

public rehash_caches
    var obj, part, element;
    
    (| .purge_caches() |);
    (> .add_object_to_local_caches(this()) <);
    if (.is($location)) {
        for obj in ([this()] + .contents())
            (> .add_object_to_remote_caches(obj) <);
    }
    shortcut_cache = .all_shortcuts();
.

public add_to_command_cache
    arg command, definer;
    
    (> .perms(sender(), 'this) <);
    if (type(command_cache) != 'dictionary)
        command_cache = #[];
    command_cache = command_cache.add_elem(command, definer);
.

public purge_caches
    (> .perms(sender()) <);
    local_cache = remote_cache = shortcut_cache = 0;
.

public add_object_to_caches
    arg obj;
    var info, thing, element, part;
    
    (> .perms(sender(), 'this) <);
    .add_object_to_local_caches();
    .add_object_to_remote_caches();
.

public add_to_remote_cache
    arg command, definer;
    var part, cmd, value, cmds, defs;
    
    (> .perms(sender(), 'this) <);
    if (type(remote_cache) != 'dictionary)
        remote_cache = #[];
    
    // if this dies, it will also fail on explode_template_word
    cmd = (| (command.explode())[1] |);
    for part in (command.explode_template_word()) {
        if (value = (| remote_cache[part] |)) {
            cmds = value[1].union([cmd]);
            defs = value[2].union([definer]);
            remote_cache = remote_cache.add(part, [cmds, defs]);
        } else {
            remote_cache = remote_cache.add(part, [[cmd], [definer]]);
        }
    }
.

public add_to_local_cache
    arg command;
    var part, cmd;
    
    (> .perms(sender(), 'this) <);
    if (type(local_cache) != 'dictionary)
        local_cache = #[];
    
    // if this dies, it will also fail on explode_template_word
    cmd = (| (command.explode())[1] |);
    for part in (command.explode_template_word())
        local_cache = local_cache.add_elem_union(part, cmd);
.

public match_in_shortcut_cache
    arg str, cmd, args;
    var shortcut, match;
    
    if (shortcut_cache) {
        for shortcut in (shortcut_cache) {
            if (match = $string.match_pattern(shortcut[1], str))
                return ['shortcut, [shortcut[2][1], [str, @$command_lib.handle_shortcut_fields(shortcut[2][2], match)]]];
        }
    }
    return 0;
.

public shortcut_cache
    return shortcut_cache;
.

public remote_cache
    return remote_cache;
.

public local_cache
    return local_cache;
.

public match_in_remote_cache
    arg str, cmd, args;
    var cache, definer, command, info, cdef, match, matched, templates;
    
    if (!remote_cache)
        return 0;
    cache = (| remote_cache[cmd] |);
    templates = [];
    matched = [];
    for command in (cache[1]) {
        for definer in (cache[2]) {
            info = definer.get_command_info('remote, command);
            if (!info)
                continue;
            for cdef in (info) {
                match = $string.match_template(cdef[2], args);
                if (match != 0)
                    matched = matched + [[match.length(), definer, [str, cmd, @match], @cdef.subrange(3)]];
            }
            templates = templates.union(info.slice(3));
        }
    }
    if (matched) {
        info = [matched[1]];
        matched = matched.delete(1);
        for match in (matched) {
            if (match[1] > info[1][1])
                info = [match];
            else if (match[1] == info[1][1])
                info = info + [match];
        }
        return ['remote, info];
    }
    return ['partial, templates];
.

public match_in_local_cache
    arg str, cmd, args;
    var command, match, matched, templates, info, cdef;
    
    if (!local_cache)
        return 0;
    templates = matched = [];
    for command in ((| local_cache[cmd] |) || []) {
        info = .get_command_info('local, command);
        if (!info)
            continue;
        for cdef in (info) {
            match = $string.match_template(cdef[2], args);
            if (match != 0)
                matched = matched + [[match.length(), [str, cmd, @match], @cdef.subrange(3)]];
        }
        templates = templates.union(info.slice(3));
    }
    if (matched) {
        info = [matched[1]];
        matched = matched.delete(1);
        for match in (matched) {
            if (match[1] > info[1][1])
                info = [match];
            else if (match[1] == info[1][1])
                info = info + [match];
        }
        return ['local, info];
    }
    if (!templates)
        return 0;
    return ['partial, templates];
.

public add_object_to_local_caches
    arg obj;
    var info, thing, element, part;
    
    (> .perms(sender(), 'this) <);
    info = (| obj.all_local_commands() |);
    if (info) {
        for element in (info) {
            for part in (element[2])
                .add_to_local_cache(part[1]);
        }
    }
.

public add_object_to_remote_caches
    arg obj;
    var info, thing, element, part;
    
    //  (> .perms(sender(), 'this) <);
    info = (| obj.all_remote_commands() |);
    if (info) {
        for element in (info) {
            for part in (element[2])
                .add_to_remote_cache(part[1], element[1]);
        }
    }
.



parent $physical
parent $has_messages
object $exit

var $exit source $void
var $exit dest $void
var $exit lock <$true_lock_frob, []>
var $exit coordinates 0
var $root child_index 260
var $root fertile 1
var $root inited 1
var $root objname 'exit
var $root created_on 796268969
var $root flags ['parameters, 'methods, 'code, 'fertile, 'core]
var $messaged verbs #[["@lock %this with *", ['lock_vrb, 'remote]]]
var $gendered gender $gender_neuter
var $described prose []
var $named name ['uniq, "Generic Exit", "the Generic Exit"]
var $named name_aliases []
var $has_commands remote #[["@lock", [["@lock", "*", "@lock <this>", 'lock_cmd, #[[1, ['this, []]]]], ["@lock", "* with|to *", "@lock <this> with|to <any>", 'lock_with_cmd, #[[1, ['this, []]], [3, ['any, []]]]]]], ["@unlock", [["@unlock", "*", "@unlock <this>", 'unlock_cmd, #[[1, ['this, []]]]]]]]
var $has_messages messages #[[$exit, #[["exit.actor", <$ctext2_frob, [["You take ", <$ctext_generator, ["exit", #[], #[], [], 'gen_exit]>, "."], #[['line, 1], ['this, $exit]]]>], ["exit.source", <$ctext2_frob, [[<$ctext_generator, ["actor", #[], #[], [], 'gen_actor]>, " goes through ", <$ctext_generator, ["exit", #[], #[], [], 'gen_exit]>, "."], #[['line, 1], ['this, $exit]]]>], ["exit.dest", <$ctext2_frob, [[<$ctext_generator, ["actor", #[], #[], [], 'gen_actor]>, " arrives."], #[['line, 1], ['this, $exit]]]>]]]]
var $has_messages message_info #[["exit", #[]]]

root init_exit
    source = $places.place('default);
    dest = source;
    source.add_exit(0, 0);
    lock = $true_lock_frob.new();
.

root uninit_exit
    (| source.del_exit() |);
    (| source.did_detach() |);
    (| dest.did_detach() |);
    source = 0;
    dest = 0;
    lock = 0;
.

public environment
    return [this()] + source.environment().setremove(this());
.

public invoke
    arg [flags];
    var a, here, vars, m;
    
    a = sender();
    flags = [@flags, #[]][1];
    flags = flags.add('actor, a);
    flags = flags.add_elem('exclude, a);
    if (!valid(.dest()))
        $parse_lib.tell_error(.name() + " has an invalid destination, notify the manager (" + .manager().namef('ref) + ").", "", a);
    if (!lock.try(sender()))
        return a.tell(.name() + " is locked.");
    vars = #[["$actor", a], ["actor", a.name()], ["$source", .source()], ["source", .source().name()], ["$dest", .dest()], ["dest", .dest().name()], ["$exit", this()], ["exit", .name()]];
    m = .eval_message("exit", vars, $exit);
    .dest().announce(m);
    .source().announce(m);
    a.move_to(.dest());
    a.tell(.dest().get_description(flags));
    $movement_event.new(a, .source(), .dest()).dispatch();
.

public lock
    return lock;
.

public attach: disallow_overrides
    arg source_place, dest_place, radial, azimuth;
    
    // radial/azimuth coordinates.
    (> .perms(sender()) <);
    (> $places.is_place(source_place) <);
    (> $places.is_place(dest_place) <);
    if (source_place == source)
        return;
    (> source_place.will_attach('source, sender()) <);
    (> dest_place.will_attach('dest, sender()) <);
    (| source.del_exit() |);
    source = source_place;
    dest = dest_place;
    coordinates = [radial, azimuth];
    source_place.add_exit(radial, azimuth);
    (| source_place.did_attach('source, sender()) |);
    (| dest_place.did_attach('dest, sender()) |);
.

public dest
    return dest;
.

public source
    return source;
.

public place_destroyed
    arg place;
    
    (> .perms(caller(), $place) <);
    if (sender() in [dest, source])
        .destroy();
.

public short_description
    arg actor, [exclude];
    var prose;
    
    return .long_description(actor, @exclude);
.

public long_description
    arg actor, [exclude];
    var prose;
    
    prose = .prose('literal);
    if (!(| prose['long] |))
        return (| .dest().short_description(sender(), sender()) |) || (> pass(actor, @exclude) <);
    return (> pass(actor, @exclude) <);
.

public lock_cmd
    arg cmdstr, cmd, this;
    
    if (!(| .perms(sender()) |))
        return "Only " + .manager().name() + " can lock " + .name('def) + "!";
    lock = $false_lock_frob.new();
    sender().tell("You lock " + .name('def));
.

public lock_with_cmd
    arg cmdstr, cmd, this, prep, str;
    
    if (!(| .perms(sender()) |))
        return "Only " + .manager().name() + " can lock " + .name('def) + "!";
    catch ~objnf, ~parse {
        lock = $lock_parser.parse(str, sender());
        return "You lock " + .name('def) + " to allow " + lock.lock_name('exit) + ".";
    } with handler {
        switch (error()) {
            case ~objnf:
                return "Object not found in lock string.";
            case ~parse:
                return "Invalid lock string.";
        }
    }
.

public unlock_cmd
    arg cmdstr, cmd, this;
    
    if (!(| .perms(sender()) |))
        return "Only " + .manager().name() + " can lock " + .name('def) + "!";
    lock = $true_lock_frob.new();
    sender().tell("You unlock " + .name('def));
.

public is_visible_to
    arg whom;
    
    return .visibility() >= whom.location().darkness();
.



parent $physical
object $located

var $located inited 0
var $located location $nowhere
var $located obvious 1
var $root objname 'located
var $root created_on 796268969
var $root flags ['parameters, 'methods, 'code, 'core]
var $messaged verbs #[]
var $gendered gender $gender_neuter
var $described prose []
var $named name ['uniq, "Generic Located Object", "the Generic Located Object"]
var $named name_aliases []

root init_located
    location = $nowhere;
    location.add_sender_to_contents();
    obvious = 1;
.

public uninit
    if (caller() != $root)
        throw(~perm, "Caller is not root.");
    location.del_sender_from_contents();
    location = 0;
.

public environment
    return [this()] + location.environment().setremove(this());
.

public match_environment
    arg str;
    var thing, matches;
    
    if (str == "here") {
        return location;
    } else if (str in ["everyone", "everybody", "everything"]) {
        matches = [];
        if (str in ["everyone", "everybody"]) {
            for thing in (.location().contents().setremove(this())) {
                if (thing.has_ancestor($user))
                    matches = [@matches, thing];
            }
        } else {
            matches = .location().contents();
        }
        if (matches.length() > 1)
            throw(~ambig, "Several matches.", matches);
        else if (matches)
            return matches[1];
        else
            throw(~objnf, "No matches.");
    } else {
        return (> pass(str) <);
    }
.

public location
    return location || $void;
.

public will_move
    arg mover, place;
    
.

public did_move
    arg mover, old_place;
    
    if (caller() != definer() || sender() != this())
        throw(~perm, "Invalid call to protected method.");
.

public realm
    return realm;
.

root uninit_located
    .location().del_sender_from_contents();
.

public move_to
    arg place;
    var old;
    
    // Don't do anything if we're already here.
    if (place == location)
        return;
    if (!place.has_ancestor($location))
        throw(~type, "Argument is not a location.");
    
    // Notify involved parties of impending move, allowing them to throw
    // errors.
    if (!valid(location))
        location = $nowhere;
    (> .will_move(sender(), place) <);
    (> location.will_leave(place) <);
    (> place.will_arrive(location) <);
    
    // Set location.
    old = location;
    location = place;
    old.del_sender_from_contents();
    place.add_sender_to_contents();
    
    // Notify involved parties of completed move, in reverse order.
    place.did_arrive(old);
    old.did_leave(place);
    .did_move(sender(), old);
.

public match_environment_all
    arg s;
    
    if (s == "here")
        return [location, @(> pass(@args) <)];
    else
        return (> pass(s) <);
.

public obvious
    return obvious;
.

public set_obvious
    arg obv;
    
    .perms(sender());
    obvious = obv;
.

public realm_name
    arg [args];
    
    return .location().realm_name(@args);
.

public announce
    arg [args];
    
.

public is_obvious_to
    arg whom;
    
    // will later do something creative here
    return 1;
.



parent $located
parent $public
object $thing

var $root child_index 64
var $root fertile 1
var $root inited 1
var $root objname 'thing
var $root created_on 796268969
var $root flags ['parameters, 'methods, 'code, 'fertile, 'core]
var $messaged verbs #[["take|get %this", ['take_vrb, 'noremote]], ["drop %this", ['drop_vrb, 'noremote]]]
var $located location $void
var $located obvious 1
var $gendered gender $gender_neuter
var $described prose []
var $named name ['uniq, "Generic Thing", "the Generic Thing"]
var $named name_aliases []
var $public public ['readable]
var $has_commands shortcuts #[]
var $has_commands remote #[["@lock", [["@lock", "* with|to *", "@lock <this> with|to <any>", 'lock_with_cmd, #[[1, ['this, []]], [3, ['any, []]]]], ["@lock", "*", "@lock <this>", 'lock_cmd, #[[1, ['this, []]]]]]], ["@unlock", [["@unlock", "*", "@unlock <this>", 'unlock_cmd, #[[1, ['this, []]]]]]], ["@boot", [["@boot", "*", "@boot <this>", 'boot_cmd, #[[1, ['this, []]]]]]]]
var $has_commands local #[]
var $thing lock <$object_lock_frob, [$void]>
var $has_settings defined_settings #[["home", #[['get, 'home], ['set, 'set_local_setting], ['check, 'check_home], ['del, 'delete_local_setting], ['check_args, []], ['get_args, []], ['set_args, []]]]]
var $has_settings local_settings #[]
var $has_settings settings #[]

public boot_cmd
    arg cmdstr, cmd, this;
    var loc, dest, exit;
    
    loc = .location();
    if (!(| .perms(sender(), 'manager) |)) {
        .tell(sender().name() + " tried to boot you from " + loc.name() + "!");
        loc.announce(sender().name() + " tried to boot " + .name() + " from " + loc.name() + "!", sender(), this());
        return "Only " + loc.manager().name() + " can boot people from " + loc.name() + "!";
    }
    dest = .home();
    catch any {
        sender().tell("You boot " + .namef() + ".");
        loc.announce(sender().namef() + " boots " + .namef() + " from " + .location().name('def) + ".", this(), sender());
        if (sender().location() != loc)
            sender().location().announce(sender().namef() + " boots " + .namef() + " from " + .location().name('def) + ".", this(), sender());
        (> .move_to(dest) <);
    } with handler {
        return traceback()[1][2];
    }
.

public lock
    return lock;
.

public lock_cmd
    arg cmdstr, cmd, this;
    
    if (!(| .perms(sender()) |))
        return "Only " + .manager().name() + " can lock " + .name('def) + "!";
    lock = $false_lock_frob.new();
    return "You lock " + .name('def);
.

public lock_with_cmd
    arg cmdstr, cmd, this, prep, str;
    
    if (!(| .perms(sender()) |))
        return "Only " + .manager().name() + " can lock " + .name('def) + "!";
    catch ~objnf, ~parse {
        lock = $lock_parser.parse(str, sender());
        return "You lock " + .name('def) + " " + prep + " " + lock.lock_name('thing) + ".";
    } with handler {
        switch (error()) {
            case ~objnf:
                return "Object not found in lock string.";
            case ~parse:
                return "Invalid lock string.";
        }
    }
.

public unlock_cmd
    arg cmdstr, cmd, this;
    
    if (!(| .perms(sender()) |))
        return "Only " + .manager().name() + " can lock " + .name('def) + "!";
    lock = $true_lock_frob.new();
    return "You unlock " + .name('def);
.

public will_move
    arg mover, place;
    
    (> pass(mover, place) <);
    if (mover.is($housekeeper))
        return;
    if (lock && mover != $exit && !lock.try(mover))
        throw(~locked, .name('def).capitalize() + " is locked to " + lock.lock_name('thing) + ".");
    else if (!.is_writable_by(sender()))
        throw(~move, "You cannot move " + .objname());
.

public home
    arg [args];
    
    return (| .get_local_setting("home", $thing) |) || $lost_and_found;
.

public check_home
    arg definer, value, [args];
    var home;
    
    home = (> .match_environment(value) <);
    if (!(| home.perms(sender()) |) && !(| home.setting("public-home") |))
        throw(~perm, "You do not have permission to make " + home.name() + " your home.");
    return home;
.

public check_gender
    arg definer, value, [args];
    var g, gs;
    
    gs = [$gender_female, $gender_male, $gender_neuter];
    g = value in gs.map('name);
    if (!g)
        throw(~set, "Gender must be one of: " + gs.map('name).to_english("", " or "));
    return gs[g];
.

public check_match_with
    arg definer, value, [args];
    var matching;
    
    if (value in ["regexp", "pattern", "begin"])
        return tosym("match_" + value.lowercase());
    throw(~perm, "You can match with: regexp, pattern, begin.");
.



parent $physical
parent $command_cache
object $location

var $location contents []
var $root fertile 1
var $root inited 1
var $root objname 'location
var $root created_on 796268969
var $root flags ['parameters, 'methods, 'code, 'fertile, 'core]
var $messaged verbs #[]
var $gendered gender $gender_neuter
var $described prose []
var $named name ['uniq, "Generic Container Object", "the Generic Container Object"]
var $named name_aliases []
var $command_cache shortcut_cache 0
var $command_cache remote_cache 0
var $command_cache local_cache 0
var $has_commands shortcuts #[]
var $has_settings defined_settings #[]
var $has_settings local_settings #[]
var $has_settings settings #[]

root init_location
    contents = [];
.

root uninit_location
    var obj;
    
    for obj in (contents)
        obj.move_to($nowhere);
.

public contents
    return contents || [];
.

public contains
    arg obj;
    
    return obj in .contents() ? 1 | 0;
.

public find_in_contents
    arg str;
    var obj;
    
    for obj in (.contents()) {
        if (obj.match_name(str))
            return;
    }
.

public will_arrive
    arg old_place;
    
    if (caller() != $located)
        throw(~perm, "Caller is not $located.");
.

public will_leave
    arg place;
    
    if (caller() != $located)
        throw(~perm, "Caller is not $located.");
.

public did_arrive
    arg place;
    
    if (caller() != $located)
        throw(~perm, "Caller is not $located.");
    .add_object_to_remote_caches(sender());
.

public did_leave
    arg place;
    
    if (caller() != $located)
        throw(~perm, "Caller is not $located.");
    (| .del_from_command_environment(sender()) |);
.

public add_sender_to_contents: disallow_overrides
    if (caller() != $located)
        throw(~perm, "Caller is not $located.");
    if (sender().location() != this())
        throw(~location, "Sorry, but you're not here.");
    contents = .contents().setadd(sender());
.

public del_sender_from_contents: disallow_overrides
    if (caller() != $located)
        throw(~perm, "Caller not an agent of located protocol.");
    contents = contents.setremove(sender());
.

public validate_contents
    var obj, newcont;
    
    if (!.is_writable_by(sender()))
        throw(~perm, "Must be an owner to validate contents");
    newcont = [];
    for obj in (contents) {
        if (valid(obj) && obj.has_ancestor($located) && obj.location() == this())
            newcont = newcont.setadd(obj);
    }
    contents = newcont;
.

public environment
    return [this()] + contents;
.

public add_to_contents: disallow_overrides
    arg what;
    
    if (caller() != $located)
        throw(~perm, "Caller is not $located.");
.

public contents_accept_mail
    return 1;
.

public realm
    arg [args];
    var loc;
    
    loc = "";
    if ((| .location() |))
        loc = .location().realm();
    return loc + "[" + .namef() + "]";
.

public realm_name
    return "";
.



parent $thing
parent $location
object $located_location

var $root child_index 3
var $root fertile 1
var $root inited 1
var $root objname 'located_location
var $root created_on 796268969
var $root flags ['parameters, 'methods, 'code, 'fertile, 'core]
var $messaged verbs #[]
var $location contents []
var $located location $nowhere
var $located obvious 1
var $gendered gender $gender_neuter
var $described prose []
var $named name ['uniq, "Generic Located Location", "the Generic Located Location"]
var $named name_aliases []
var $command_cache shortcut_cache 0
var $command_cache remote_cache 0
var $command_cache local_cache 0

public environment
    return pass() + .contents();
.



parent $located_location
object $body

var $root child_index 14
var $root fertile 1
var $root inited 1
var $root objname 'body
var $root created_on 796268969
var $root flags ['parameters, 'methods, 'code, 'fertile, 'core]
var $messaged verbs #[]
var $location contents []
var $located location $nowhere
var $located obvious 1
var $body body_parts #[]
var $body available_body_parts 0
var $body wearing 0
var $body following 0
var $gendered gender $gender_neuter
var $described prose []
var $named name ['uniq, "Generic Body", "the Generic Body"]
var $named name_aliases []
var $command_cache shortcut_cache 0
var $command_cache remote_cache 0
var $command_cache local_cache 0

public tell
    arg [args];
    
.

public set_body_part
    arg part, frob, param;
    
    if (sender().has_ancestor($wearable_frob))
        throw(~perm, "Sender must be $wearable_frob.");
    body_parts = body_parts.add(frob.new(part, param));
.

public wearing
    arg [args];
    var x, w;
    
    w = wearing || [];
    if (args && 'objects in args) {
        for x in [1 .. w.length()]
            w = w.replace(x, class(w[x]));
    }
    return w;
.

public body_parts
    return body_parts;
.

public namef
    arg [args];
    
    if (!args)
        args = [['name]];
    if (args[1] == 'nactivity || args[1] == 'titled)
        args = args.subrange(2);
    return pass(@args);
.

public available_body_parts
    return available_body_parts || ['head, 'rleg, 'lleg, 'rarm, 'larm, 'torso];
.

public wear
    arg frob;
    
    if (caller() != $wearable_frob)
        throw(~wear, "You can only wear descendants of $wearable_frob.");
    wearing = wearing ? [@wearing, frob] | [frob];
.

public shed
    arg name;
    var f;
    
    (> .perms(sender(), 'this) <);
    for f in (.wearing()) {
        if ($string.match_begin(f.name(), name))
            wearing = wearing.setremove(f);
    }
.

public environment
    return pass() + .wearing();
.

public will_move
    arg mover, place;
    
    // exits should always be able to pull "bodies" through them
    // this becomes sortof a big override returning, but ... *shrug*
    if (mover.is($exit))
        return;
    (> pass(mover, place) <);
.

public set_following
    arg what;
    
    (> .perms(sender()) <);
    following = what;
.

public event
    arg event;
    
    (> pass(event) <);
    if (following) {
        if (following == event.actor() && event.dest() != .location())
            event.exit().invoke(#[['prose, 0], ['extra, 0]]);
    }
.

public description
    arg flags;
    var ctext, what, w;
    
    ctext = (> pass(flags) <);
    if (w = .wearing())
        ctext = ctext + [.gender().pronoun('psc) + " is wearing " + w.map('name).to_english() + "."];
    else
        ctext = ctext + [.gender().pronoun('psc) + " is naked, baring it all to the world."];
    return ctext;
.



parent $command_cache
object $user_interfaces

var $root child_index 7
var $root fertile 1
var $root inited 1
var $root objname 'user_interfaces
var $root created_on 796268969
var $root flags ['parameters, 'methods, 'code, 'fertile, 'core]
var $has_commands shortcuts #[]
var $command_cache shortcut_cache 0
var $command_cache remote_cache 0
var $command_cache local_cache 0



parent $user_interfaces
object $interaction

var $root inited 1
var $root objname 'interaction
var $root created_on 796268969
var $root flags ['parameters, 'methods, 'code, 'core]
var $interaction stage #[[">", " -> "], ["<", " <- "], ["[", " ["], ["|", " | "]]
var $interaction last_interacted_with 0
var $interaction interaction 0
var $has_commands local #[["wh?isper", [["wh?isper", "* to *", "wh?isper <any> to <any>", 'whisper_cmd, #[[1, ['any, []]], [3, ['any, []]]]]]], ["say", [["say", "*", "say <any>", 'say_cmd, #[[1, ['any, []]]]]]], ["to", [["to", "* say *", "to <any> say <any>", 'say_to_cmd, #[[1, ['any, []]], [3, ['any, []]]]]]], ["@paste", [["@paste", "*", "@paste <any>", 'paste_cmd, #[[1, ['any, []]]]]]], ["emote", [["emote", "*", "emote <any>", 'emote_cmd, #[[1, ['any, []]]]]]], ["on", [["on", "* say *", "on <any> say <any>", 'on_subj_cmd, #[[1, ['any, []]], [3, ['any, []]]]]]], ["@to|@page", [["@to|@page", "* with|say *", "@to|@page <any> with|say <any>", 'remote_say_cmd, #[[1, ['any, []]], [3, ['any, []]]]]]], ["@emote?-to|@epage", [["@emote?-to|@epage", "* with *", "@emote?-to|@epage <any> with <any>", 'remote_emote_cmd, #[[1, ['any, []]], [3, ['any, []]]]]]]]
var $has_commands shortcuts #[["|*", ['_interact, ["|", 1]]], ["<*", ['_interact, ["<", 1]]], ["%*", ['think_cmd, ["think", 1]]], ["!*", ['spoof_cmd, ["spoof", 1]]], ["''*", ['say_to_cmd, ["to", "", "say", 1]]], ["'* *", ['say_to_cmd, ["to", 1, "say", 2]]], ["]*", ['right_encapsulate_cmd, ["]", 1]]], [")*", ['right_encapsulate_cmd, [")", 1]]], [",*, *", ['esay_cmd, ["esay", 1, "with", 2]]], [",*,*", ['esay_cmd, ["esay", 1, "with", 2]]], ["--*", ['remote_say_cmd, ["rsay", "", "with", 1]]], ["-* *", ['remote_say_cmd, ["rsay", 1, "with", 2]]], ["++*", ['remote_emote_cmd, ["@emote", "", "with", 1]]], ["+* *", ['remote_emote_cmd, ["@emote", 1, "with", 2]]], ["\"*", ['say_cmd, ["say", 1]]], [":*", ['emote_cmd, ["emote", 1]]]]
var $command_cache shortcut_cache 0
var $command_cache remote_cache 0
var $command_cache local_cache 0

public remote_say_cmd
    arg cmdstr, com, who, prep, message;
    var target, targets, line, fromline;
    
    (> .perms(sender(), 'this) <);
    targets = (> ._parse_interaction_reference(who, "say") <);
    if (targets) {
        ._add_interaction('objs, targets);
        line = .namef() + " ";
        if (message)
            line = .name() + " " + $code_lib.punctuation_type(message);
        else
            line = .name() + " say";
        line = line + "s, \"" + message + "\"";
        fromline = "[from " + .location().namef() + "] " + line;
        for target in (targets)
            (| target.tell(fromline) |);
        .tell("[to " + targets.map('name).to_english() + "] " + line);
    }
.

public polite_spoof_cmd
    arg cmd, what;
    var name;
    
    (> .perms(sender(), 'this) <);
    name = .namef();
    if (!(name + " " in what || " " + name in what))
        what = what + "     -- " + name;
    .location().announce(what);
.

public say_to_cmd
    arg cmdstr, com, who, prep, message;
    var targets, target, line;
    
    (> .perms(sender(), 'this) <);
    targets = (> ._parse_interaction_reference(who, "say") <);
    if (targets) {
        ._add_interaction('objs, targets);
        line = .namef() + " (to " + targets.map_to_english('namef) + ") ";
        if (message)
            line = line + $code_lib.punctuation_type(message);
        else
            how = line + "say";
        line = line + "s, \"" + message + "\"";
        .location().announce(line);
    }
.

public whisper_cmd
    arg cmdstr, com, what, prep, who;
    var loc;
    
    (> .perms(sender(), 'this) <);
    who = .match_env_nice(who);
    loc = .location();
    if (who.location() != loc) {
        .tell("You must be in the same room as a person, to whisper to them.");
        return;
    }
    who.tell(.namef() + " whispers, \"" + what + "\"");
    .tell("You whisper, \"" + what + "\" to " + who.namef() + ".");
    loc.announce(.namef() + " whispers to " + who.namef() + ".", who, this());
.

public _interact
    arg cmdstr, cmd, what;
    var stage;
    
    (> .perms(sender(), 'this) <);
    stage = $interaction.get_stage();
    
    // echo .namef()+stage[cmd]+what to the room
    if (cmd in stage.keys())
        .location().announce(.namef() + stage[cmd] + what);
    else
        throw(~unknowncmd, "Unknown cmd '" + cmd + "'");
.

public get_stage
    return stage;
.

public think_cmd
    arg cmdstr, cmd, what;
    
    (> .perms(sender(), 'this) <);
    .location().announce(.namef() + " . o O ( " + what + " )");
.

public emote_cmd
    arg cmdstr, com, what;
    
    if (what && what[1] == ":")
        .location().announce(.name() + what.subrange(2));
    else
        .location().announce(.name() + " " + what);
.

public say_cmd
    arg cmdstr, cmd, what;
    var type, how, idx;
    
    (> .perms(sender(), 'this) <);
    if (what)
        how = $code_lib.punctuation_type(what);
    else
        how = "say";
    .location().announce(.name() + " " + how + "s, \"" + what + "\"");
.

public paste_cmd
    arg cmdstr, com, [who];
    var obj, text;
    
    (> .perms(sender(), 'this) <);
    who = [@who, 0][1];
    if (who) {
        who = $list.to_string(who.explode().subrange(2));
        obj = (| .find_object(who, 'environment, 'environment, 'grasp) |);
        if (!obj)
            .tell_error(com + "[to <user>]", "No user found by the name \"" + who + "\".");
    }
    text = .read();
    if (text == 'aborted)
        return .tell("@paste aborted.");
    else if (!text)
        return .tell("@paste nothing?");
    text = [$string.center(" " + .namef() + " (@paste's) ", 79, "-"), @text, $string.center(" + Finis + ", 79, "-")];
    if (obj) {
        obj.tell(text);
        .tell("Text pasted to " + who.namef() + ".");
    } else {
        .location().announce(text);
    }
.

public esay_cmd
    arg cmdstr, cmd, how, prep, what;
    
    (> .perms(sender(), 'this) <);
    .location().announce(.name() + " " + how + ", \"" + what + "\"");
.

public page_emote_cmd
    arg cmdstr, com, who, prep, message;
    var user, recipient, targets, name;
    
    (> .perms(sender(), 'this) <);
    who = who && $string.explode_english_list(who) || [];
    if (!who) {
        if (last_interacted_with)
            who = last_interacted_with[2];
        else
            $parse_lib.tell_error("You must specify somebody or something to speak to.");
    }
    targets = who;
    name = .namef() + " ";
    for recipient in (who) {
        if (type(recipient) == 'dbref) {
            user = recipient;
        } else {
            user = (| $user_db.match_begin(recipient) |);
            if (!user)
                user = (| .match_environment(recipient) |);
        }
        if (user && user.has_ancestor($user) && user.connected()) {
            user.tell("[from " + .location().namef() + "] " + name + message);
            who = who.replace(recipient in who, user.namef());
            targets = targets.replace(recipient in targets, user);
        } else {
            .tell("! " + $data_lib.unparse(recipient) + " is either not connected, not a user, or misspelled.");
            who = who.setremove(recipient);
            targets = targets.setremove(recipient);
        }
    }
    if (targets) {
        last_interacted_with = [time(), targets];
        who = who.to_english();
        .tell("You epage \"" + name + message.chop(.linelen() - 17 - who.length()) + "\" to " + who + ".");
    }
.

public right_encapsulate_cmd
    arg cmdstr, right, line;
    var space, line, left, right;
    
    (> .perms(sender(), 'this) <);
    space = " ";
    if (!line)
        space = "";
    switch (right) {
        case "]":
            left = "[";
        case ")":
            left = "(";
    }
    .location().announce(left + .namef() + space + line + right);
.

public remote_emote_cmd
    arg cmdstr, com, who, prep, message;
    var target, targets, line, fline;
    
    (> .perms(sender(), 'this) <);
    targets = (> ._parse_interaction_reference(who, "emote") <);
    if (targets) {
        ._add_interaction('objs, targets);
        line = .namef() + " " + message;
        fline = "[from " + .location().namef() + "] " + line;
        for target in (targets)
            (| target.tell(fline) |);
        .tell("[to " + targets.map('name).to_english() + "] " + line);
    }
.

public _parse_interaction_reference
    arg targets, what, [syn];
    var recip, target;
    
    (> .perms(sender(), 'this) <);
    syn = [@syn, ""][1];
    targets = targets && targets.explode_list() || [];
    if (!targets) {
        targets = ._get_interaction('objs) || .tell_error(syn, "You must direct your " + what + ".");
        targets = targets[2];
    } else {
        for recip in (targets) {
            target = (| $user_db.match_begin(recip) |) || (| .match_environment(recip) |);
            if (target) {
                targets = targets.replace(recip in targets, target);
            } else {
                .tell("Unable to find " + toliteral(recip) + ".");
                targets = targets.setremove(recip);
            }
        }
    }
    return targets;
.

public _get_interaction
    arg key;
    
    (> .perms(sender(), 'this) <);
    return (| interaction[key] |) || 0;
.

public _add_interaction
    arg key, value;
    
    (> .perms(sender(), 'this) <);
    if (!interaction)
        interaction = #[];
    if (type(value) != 'list)
        value = [value];
    value = [time(), value];
    interaction = interaction.add(key, value);
.

public _purge_interaction
    interaction = 0;
.



parent $misc
object $mail_root

var $root child_index 2
var $root fertile 1
var $root trusted []
var $root inited 1
var $root objname 'mail_root
var $root created_on 796268969
var $root indestructable 1
var $root flags ['parameters, 'methods, 'code, 'fertile, 'core]



parent $mail_root
parent $named
object $mail_list

var $root child_index 2
var $root inited 1
var $root objname 'mail_list
var $root created_on 796268969
var $root indestructable 1
var $root flags ['parameters, 'methods, 'code, 'core]
var $named name_aliases []
var $named name ['uniq, "mail_list", "the mail_list"]
var $messaged verbs 0
var $mail_list notify [$mail_list]
var $mail_list last_received_on 0
var $mail_list senders 1
var $mail_list mail []
var $mail_list readers 1
var $public public ['readable]
var $gendered gender $gender_neuter
var $described prose #[]

public del_sender_from_notification
    var who;
    
    who = sender();
    if (!who.has_ancestor($user))
        throw(~type, "Sender is not a user.");
    if (!.has_flag('sender, who))
        throw(~perm, who.name() + " cannot read " + .mail_name());
    notify = notify.setremove(who);
.

public add_sender_to_notification
    var who;
    
    who = sender();
    if (!who.has_ancestor($user))
        throw(~type, "Sender is not a user.");
    if (!.has_flag('sender, who))
        throw(~perm, who.name() + " cannot read " + .mail_name());
    notify = [@notify, who];
.

public list_is_sendable_by
    arg who;
    
    if (.is_writable_by(who))
        return 1;
    if (type(senders) == 'list)
        return who in senders;
    return senders;
.

public list_is_readable_by
    arg who;
    
    if (.is_writable_by(who))
        return 1;
    if (type(readers) == 'list)
        return who in readers;
    return readers;
.

public set_name
    arg new_name, [args];
    var old_name;
    
    old_name = .name();
    if (new_name && new_name[1] == "*")
        new_name = new_name.subrange(2);
    (> pass(new_name, @args) <);
    (| $mail_db.key_changed(old_name, new_name) |);
.

public start
    if (!.list_is_readable_by(sender()))
        throw(~perm, "Sender cannot read " + .mail_name() + ".");
    if (mail)
        return mail[1];
    return 0;
.

public last_received_on
    return last_received_on;
.

public recent_mail
    arg [diff];
    
    if (!.list_is_readable_by(sender()))
        throw(~perm, "Sender cannot read " + .mail_name() + ".");
    diff = [@diff, 20][1];
    if (mail.length() < diff)
        return [0, mail];
    return [mail.length() - diff - 1, mail.subrange(mail.length() - diff)];
.

public set_notify
    arg new_value;
    
    (> .perms(sender(), 'manager) <);
    if (type(new_value) != 'integer && type(new_value) != 'list)
        throw(~type, "new value must be submitted as a list of users or boolean integer.");
    notify = new_value;
.

public set_senders
    arg new_value;
    
    (> .perms(sender(), 'manager) <);
    if (type(new_value) != 'integer && type(new_value) != 'list)
        throw(~type, "new value must be submitted as a list of users or boolean integer.");
    senders = new_value;
.

public notify
    (> .perms(sender(), 'manager) <);
    return notify;
.

public del_mail
    arg old_mail, [sender];
    
    // what the hell am I thinking?
    sender = [@sender, sender()][1];
    if (!$mail_lib.has_mail_perms(caller()))
        throw(~perm, caller().namef('xref) + " cannot remove " + old_mail.mail_name());
    old_mail.del_recipient(this());
    mail = mail.del(old_mail);
.

public _announce_new_mail
    arg new_mail;
    var line, who, n;
    
    (> .perms(sender(), 'this) <);
    if (!notify)
        return;
    line = .mail_name() + " has been sent new mail by " + new_mail.from().namef() + ": " + new_mail.subject();
    for who in (notify)
        (| who.tell(line.chop(who.linelen())) |);
.

public mail
    //  if (!.list_is_sendable_by(sender()))
    //      throw(~perm, "Sender cannot read " + .mail_name() + ".");
    return mail;
.

public add_mail
    var new_mail;
    
    (> .perms(caller(), $mail_message) <);
    last_received_on = time();
    new_mail = sender();
    
    // make sure we do not already have it
    if (new_mail in mail)
        return;
    
    // add it
    mail = mail.add(new_mail);
    ._announce_new_mail(new_mail);
.

public senders
    (> .perms(sender(), 'manager) <);
    return senders;
.

public mail_name
    return $mail_lib.mail_name(this());
.

root init_mail_list
    mail = [];
    senders = 1;
    readers = [.manager()];
    notify = [.manager()];
    if (!.has_ancestor($user)) {
        readers = 1;
        (| $mail_db.insert(.name(), this()) |);
    } else {
        readers = [.manager()];
    }
.

root uninit_mail_list
    var m;
    
    for m in (mail)
        .del_mail(let[1]);
    mail = [];
    senders = 1;
    readers = [.manager()];
    notify = [];
    if (!.has_ancestor($user))
        (| $mail_db.remove(.name()) |);
.

public next
    arg current_mail;
    
    if (!.list_is_readable_by(sender()))
        throw(~perm, "Sender cannot read this list.");
    return mail[(current_mail in mail) + 1];
.

public mail_list_prev
    arg current_mail;
    
    if (!.list_is_readable_by(sender()))
        throw(~perm, "Sender cannot read this list.");
    return mail[(current_mail in mail) - 1];
.



parent $mail_list
parent $user_interfaces
object $mail_ui

var $root inited 1
var $root objname 'mail_ui
var $root created_on 796268969
var $root indestructable 1
var $root flags ['parameters, 'methods, 'code, 'core]
var $mail_ui subscribed #[]
var $mail_ui current 0
var $mail_list letters #[]
var $mail_list letters_index #[]
var $mail_list senders 1
var $mail_list readers []
var $mail_list notify [$mail_ui]
var $mail_list last_letter 0
var $mail_list mail []
var $gendered gender $gender_neuter
var $described prose #[]
var $has_commands local #[["@mail|@send", [["@mail|@send", "* to *", "@mail|@send <any> to <any>", 'mail_to_cmd, #[[1, ['any, []]], [3, ['any, []]]]]]], ["@sub?scribed", [["@sub?scribed", "*", "@sub?scribed <any>", 'subscribe_cmd, #[[1, ['any, []]]]]]], ["@unsub?scribed", [["@unsub?scribed", "*", "@unsub?scribed <any>", 'unsubscribe_cmd, #[[1, ['any, []]]]]]], ["@mail-list?s", [["@mail-list?s", "", "@mail-list?s", 'mail_lists_cmd, #[]]]], ["@mail", [["@mail", "*", "@mail <any>", 'mail_on_cmd, #[[1, ['any, []]]]]]], ["@read", [["@read", "*", "@read <any>", 'mail_read_cmd, #[[1, ['any, []]]]]]], ["@remove-m?ail|@rmm?ail", [["@remove-m?ail|@rmm?ail", "*", "@remove-m?ail|@rmm?ail <any>", 'mail_remove_cmd, #[[1, ['any, []]]]]]]]
var $has_commands shortcuts #[]
var $named name ['prop, "Mail User Interface", "Mail User Interface"]
var $command_cache shortcut_cache 0
var $command_cache remote_cache 0
var $command_cache local_cache 0

public mail_to_cmd
    arg cmdstr, cmd, note, prep, args;
    var subj, lists, list, x, mail, text;
    
    (> .perms(sender(), 'this) <);
    lists = [];
    for x in (args.explode_english_list()) {
        catch ~listnf {
            list = $mail_lib.match_mail_recipient(x);
            lists = [@lists, list];
        } with handler {
            .tell("The list \"" + x + "\" is invalid.");
        }
    }
    if (!lists)
        return "No valid lists are specified!";
    if (note) {
        text = .match_env_nice(note).text();
    } else {
        text = .read("-- Enter text for mail message, \".\" when done or \"@abort\" to abort --");
        if (text == 'aborted)
            return;
    }
    subj = .prompt("Subject: ");
    if (subj == "@abort")
        return "** Aborted mail send **";
    mail = $mail_message.new_mail();
    mail.set_subject(subj);
    mail.set_text(text);
    catch any {
        mail.send(@lists);
    } with handler {
        .tell(traceback()[1][2]);
    }
    .tell("Mail sent.");
.

public mail_read_cmd
    arg cmdstr, cmd, str;
    var mail, x, args, lname, list, rng;
    
    (> .perms(sender(), 'this) <);
    if (args = $string.match_template("* on *", str)) {
        rng = args[1];
        list = args[3] && (| $mail_lib.match_mail_recipient(args[3]) |) || .current_mail_list();
    } else if (str) {
        rng = str;
        list = .current_mail_list();
    } else {
        rng = "";
        list = .current_mail_list();
    }
    .new_list(list);
    if (rng) {
        if (rng == "next") {
            mail = (| list.next(.current_mail_location()) |);
            if (!mail)
                .tell("No next message.");
            else
                .read_mail(mail, mail in list.mail(), list.mail_name());
            return;
        }
        if (rng == "prev") {
            mail = (| list.mail_list_prev(.current_mail_location()) |);
            if (!mail)
                .tell("No previous message.");
            else
                .read_mail(mail, mail in list.mail(), list.mail_name());
            return;
        }
        catch ~range {
            rng = $mail_lib.range_to_actual($parse_lib.range(rng), current);
        } with handler {
            .tell(traceback()[1][2]);
            return;
        }
    }
    mail = list.mail();
    lname = list.mail_name();
    if (rng) {
        for x in [rng[1] .. rng[2]] {
            if (x > mail.length())
                break;
            .read_mail(mail[x], x, lname);
        }
    } else if (current['location]) {
        .read_mail(current['location], current['location] in mail, lname);
    } else {
        .tell("List " + lname + " has no mail on it.");
    }
.

public mail_remove_cmd
    arg cmdstr, cmd, str;
    var mail, args, list, rng, all_mail, x, name;
    
    (> .perms(sender(), 'this) <);
    args = ._parse_mail_reference(str, "from");
    list = args[2];
    rng = args[1];
    .new_list(list);
    if (rng) {
        catch ~range {
            mail = (| $object_lib.to_dbref(rng) |);
            if (!mail) {
                rng = $mail_lib.range_to_actual($parse_lib.range(rng), current);
                all_mail = list.mail();
                mail = [];
                for x in [rng[1] .. rng[2]] {
                    if (x > all_mail.length())
                        break;
                    mail = [@mail, all_mail[x]];
                }
            } else {
                mail = [mail];
            }
        } with handler {
            .tell(traceback()[1][2]);
            return;
        }
    } else {
        mail = [current['location]];
    }
    for x in (mail) {
        catch ~perm {
            name = x.name();
            list.del_mail(x);
            .tell("Removed mail message " + name + " from " + list.mail_name() + ".");
        } with handler {
            .tell(traceback()[1][2]);
        }
    }
.

public read_mail
    arg mail, loc, list_name;
    
    (> .perms(sender(), 'this) <);
    .tell("Message %l (%l) on %s:".format(loc, mail.name(), list_name));
    .tell(mail.format());
    mail.did_read();
    current = current.add('location, mail);
.

public subscribed
    (> .perms(sender(), 'this) <);
    return subscribed;
.

public unsubscribe_cmd
    arg cmdstr, com, str;
    var list, line, mname;
    
    (> .perms(sender(), 'this) <);
    list = .match_mail_recipient(str);
    if (list == this())
        return .tell("You cannot unsubscribe yourself.");
    mname = .mail_name(listr);
    if (!(list in subscribed.keys()))
        return .tell("You are not subscribed to " + mname);
    .unsubscribe(list);
    .tell("Successfully unsubscribed from" + mname + ".");
.

public subscribe_cmd
    arg cmdstr, cmd, str;
    var list, mname, l, args, line, len, out;
    
    (> .perms(sender(), 'this) <);
    
    // this is ugly bad bad
    args = $parse_lib.getopt(str, [["n?ew"]]);
    if (!args[1]) {
        if ("n?ew" in args[2].slice(1)) {
            out = [];
            for l in (subscribed.keys().setremove(this())) {
                if (l.last_received_on() > subscribed[l])
                    out = out = ["  " + l.mail_name()];
            }
            if (out)
                .tell(["New mail on:"] + out);
            return;
        }
        .tell("Currently Subscribed Lists:");
        len = .linelen() / 3;
        for l in (subscribed.keys().setremove(this())) {
            line = "  " + l.mail_name();
            if (l.last_received_on() > subscribed[l])
                line = line + " (new mail)";
            .tell(line);
        }
        return;
    }
    list = $mail_lib.match_mail_recipient(str);
    mname = $mail_lib.mail_name(list);
    if (list in subscribed.keys())
        return .tell("You are already subscribed to " + mname + ".");
    if (!list.list_is_readable_by(this()))
        return .tell(mname + " is not subscribeable by you.");
    .subscribe(list);
    .tell("Successfully subscribed to " + mname + ".");
.

public subscribe
    arg list;
    
    (> .perms(sender(), 'this) <);
    if (!subscribed)
        subscribed = #[];
    subscribed = subscribed.add(list, time());
    (| list.add_sender_to_notification() |);
.

public unsubscribe
    arg list;
    
    (> .perms(sender(), 'this) <);
    subscribed = subscribed.del(list);
    (| list.del_sender_from_notification() |);
.

public mail_lists_cmd
    arg [args];
    var l, line;
    
    (> .perms(sender(), 'this) <);
    for l in ($mail_db.database().values()) {
        line = "";
        if (l.list_is_readable_by(this()))
            line = "[Readable]";
        if (l.list_is_sendable_by(this()))
            line = "[Sendable]" + (line ? " " | "") + line;
        .tell(l.mail_name().pad(.linelen() - line.length() - 1) + " " + line);
    }
.

public new_list
    arg list;
    
    (> .perms(sender(), 'this) <);
    if (list in subscribed.keys())
        subscribed = subscribed.add(list, time());
    if (list != current['list])
        current = current.add('list, list);
    if (!valid(current['location]))
        current = current.add('location, list.start());
.

root init_mail_ui
    current = #[['location, .start()], ['list, this()]];
    .subscribe(this());
    (| .subscribe($mail_list_news) |);
    .new_list(this());
.

public mail_on_cmd
    arg cmdstr, cmd, str;
    var syn, args, r, all_mail, mail, rng, x, line, list;
    
    (> .perms(sender(), 'this) <);
    syn = cmd + " [<range> on <mail list>]";
    if (args = $string.match_template("* on *", str)) {
        rng = args[1];
        list = args[3] && $mail_lib.match_mail_recipient(args[3]) || this();
    } else if (args = (| $mail_lib.match_mail_recipient(str) |)) {
        return .mail_to_cmd(cmdstr, cmd, "", "", str);
    } else {
        rng = "";
        list = current['list];
    }
    if (!list.list_is_readable_by(this()))
        return "You cannot read mail on " + list.mail_name();
    catch any {
        if (list != current['list])
            .new_list(list);
        all_mail = current['list].mail();
        if (!all_mail) {
            .tell("No mail on " + $mail_lib.mail_name(list) + ".");
            return;
        }
        if (!rng) {
            r = [1, all_mail.length()];
            rng = r;
        } else {
            rng = $parse_lib.range(rng);
            r = $mail_lib.range_to_actual(rng, current);
        }
    } with handler {
        switch (error()) {
            case ~listnf, ~range:
                .tell(traceback()[1][2]);
            default:
                rethrow(error());
        }
        return;
    }
    line = "Mail";
    if (r[1] != r[2])
        line = line + " from " + tostr(rng[1]) + " to " + tostr(rng[2]);
    line = line + " on " + $mail_lib.mail_name(current['list]) + ":";
    .tell(line);
    for x in [r[1] .. r[2]] {
        mail = all_mail[x];
        line = mail == current['location] ? "=>" | "  ";
        line = line + tostr(x).pad(-3) + ":";
        line = line + (mail.has_read(this()) ? " " | "!") + " ";
        line = line + mail.subject().pad(.linelen() - 38) + " ";
        line = line + mail.from().name().pad(19) + " ";
        line = line + $time.ldate(mail.time(), 'date).pad(9);
        .tell(line);
    }
    .tell("------");
.

public _parse_mail_reference
    arg str, prep;
    var args;
    
    if (args = $string.match_template("* " + prep + " *", str))
        return [args[1], args[3] && (| $mail_lib.match_mail_recipient(args[3]) |) || current['list]];
    return ["", current['list]];
.

public current_mail_location
    (> .perms(sender()) <);
    if (!(| valid(current['location]) |))
        current = current.add('location, .start());
    return current['location];
.

public current_mail_list
    (> .perms(sender()) <);
    if (!(| valid(current['list]) |))
        current = current.add('location, this());
    return current['list];
.



parent $user_interfaces
object $command_aliases

var $root inited 1
var $root objname 'command_aliases
var $root created_on 796268969
var $root flags ['parameters, 'methods, 'code, 'core]
var $command_aliases command_aliases []
var $command_cache shortcut_cache 0
var $command_cache remote_cache 0
var $command_cache local_cache 0

root init_command_aliases
    command_aliases = [];
.

root uninit_command_aliases
    command_aliases = [];
.

public command_aliases
    return command_aliases;
.

public all_command_aliases
    var user, aliases, userc;
    
    // Collect complete command alias list from ancestors.
    aliases = [];
    for user in (.ancestors()) {
        userc = (| user.command_aliases() |);
        if (userc)
            aliases = aliases + userc;
        if (user == definer())
            break;
    }
    return aliases;
.

public match_command_aliases
    arg str;
    var alias, argf, match, newstr;
    
    // attempts to rebuild the string for an alias.
    if (sender() != this())
        throw(~perm, "Sender is not this.");
    for alias in (.all_command_aliases()) {
        match = $string.match_pattern(alias[1], str);
        if (match != 0) {
            newstr = alias[2];
            for argf in [1 .. match.length()]
                newstr = newstr.replace("%" + tostr(argf), match[argf]);
            return newstr;
        }
    }
    return str;
.

public add_command_alias
    arg alias, actual;
    var index, a;
    
    (> .perms(sender()) <);
    if (type(alias) != 'string || type(actual) != 'string)
        throw(~type, "alias and actual are not strings.");
    while ("%" in alias) {
        alias = alias.replace("%" + tostr(index), "*");
        index = index + 1;
    }
    
    // have it 'replace' the old alias (if one exists) by first removing
    // the old one, and adding the new one later.
    for a in (command_aliases) {
        if (a[1] == alias)
            command_aliases = command_aliases.setremove(a);
    }
    command_aliases = [@command_aliases, [alias, actual]];
.

public del_command_alias
    arg alias;
    var ca;
    
    (> .perms(sender()) <);
    if (type(alias) != 'string)
        throw(~type, "alias is not a string.");
    for ca in (command_aliases) {
        if (ca[1] == alias) {
            command_aliases = command_aliases.setremove(ca);
            return;
        }
    }
    throw(~aliasnf, "alias `" + alias + "' is not found");
.



parent $user_interfaces
object $bad_commands

var $root inited 1
var $root objname 'bad_commands
var $root created_on 796268969
var $root flags ['parameters, 'methods, 'code, 'core]
var $bad_commands non_supported_cmds #[["quit", "@quit"], ["WHO", "@who"], ["@create", "@spawn"], ["@dig", "@build"], ["help", "@help"], ["news", "@news"], ["page", "@page"], ["@gender", "@set gender"], ["uptime", "@uptime"]]
var $has_commands local #[["@create", [["@create", "*", "@create <any>", 'old_command_cmd, #[[1, ['any, []]]]]]], ["@dig", [["@dig", "*", "@dig <any>", 'old_command_cmd, #[[1, ['any, []]]]]]], ["help", [["help", "*", "help <any>", 'old_command_cmd, #[[1, ['any, []]]]]]], ["page", [["page", "*", "page <any>", 'old_command_cmd, #[[1, ['any, []]]]]]], ["who", [["who", "*", "who <any>", 'old_command_cmd, #[[1, ['any, []]]]]]], ["quit", [["quit", "*", "quit <any>", 'old_command_cmd, #[[1, ['any, []]]]]]], ["news", [["news", "*", "news <any>", 'old_command_cmd, #[[1, ['any, []]]]]]], ["@gender", [["@gender", "*", "@gender <any>", 'old_command_cmd, #[[1, ['any, []]]]]]], ["uptime", [["uptime", "*", "uptime <any>", 'old_command_cmd, #[[1, ['any, []]]]]]]]
var $command_cache shortcut_cache 0
var $command_cache remote_cache 0
var $command_cache local_cache 0

public mangled_command
    arg str;
    
.

public add_old_cmd_reference
    arg oldcmd, [newcmd];
    
    (> .perms(sender(), 'admin) <);
    if (this() != $bad_commands)
        throw(~perm, "Only define bad commands on $bad_commands");
    if (newcmd)
        non_supported_cmds = non_supported_cmds.add(oldcmd, newcmd[1]);
    .add_command(oldcmd, 'old_command_cmd);
.

public del_old_cmd_reference
.

public old_command_cmd
    arg cmdstr, com, [args];
    var line, equiv, pref;
    
    (> .perms(sender(), 'this) <);
    equiv = (| ($bad_commands.non_supported_cmds())[com] |);
    line = "Oops, `" + com + "` is not supported here.";
    if (equiv)
        line = line + "  Try `" + equiv + "`";
    .tell(line);
    .tell("Use `@help commands` for an explanation on the differences in commands.");
.

public non_supported_cmds
    return non_supported_cmds;
.



parent $user_interfaces
parent $has_settings
object $user_data

var $root inited 1
var $root objname 'user_data
var $root created_on 796268969
var $root flags ['parameters, 'methods, 'code, 'core]
var $user_data valid_settings #[["real-name", 'real_name], ["email", 'email], ["address", 'address], ["affiliation", 'affiliation], ["position", 'position], ["location", 'location], ["interests", 'interests], ["plan", 'plan], ["projects", 'projects], ["see-also", 'see_also]]
var $user_data user_data #[]
var $messaged verbs #[]
var $has_commands local #[["@finger-data|@user-data", [["@finger-data|@user-data", "* my * is|are *", "@finger-data|@user-data <any> my <any> is|are <any>", 'data_is_cmd, #[[1, ['any, []]], [3, ['any, []]], [5, ['any, []]]]]]], ["@finger|@user-data", [["@finger|@user-data", "*", "@finger|@user-data <any>", 'data_cmd, #[[1, ['any, []]]]]]], ["@set-finger|@set-user-data", [["@set-finger|@set-user-data", "* as|to *", "@set-finger|@set-user-data <any> as|to <any>", 'set_info_cmd, #[[1, ['any, []]], [3, ['any, []]]]]]]]
var $command_cache shortcut_cache 0
var $command_cache remote_cache 0
var $command_cache local_cache 0
var $has_settings defined_settings #[]
var $has_settings local_settings #[]
var $has_settings settings #[]

public data_on
    arg data;
    
    data = (| user_data[data] |);
    if (data) {
        if (data[1] || .is_writable_by(sender()))
            return data[2];
        return "not public.";
    } else {
        return "not set.";
    }
.

public set_data
    arg key, value, public;
    
    (> .perms(sender(), 'this) <);
    if (!value) {
        if ((| user_data[key] |))
            user_data = (| user_data.del(key) |);
    } else {
        user_data = user_data.add(key, [public, value]);
    }
.

root init_user_data
    user_data = #[['real_name, [1, "???"]], ['email, [1, "???"]]];
.

public valid_settings
    return valid_settings;
.

public data_is_cmd
    arg cmdstr, cmd, what, is, args;
    var valid, line, syntax, value, public;
    
    (> .perms(sender(), 'this) <);
    valid = $user_data.valid_settings();
    what = (| valid[what] |);
    if (!what) {
        syntax = cmd + " <setting name> is|to <value> [+/-public]";
        lines = ["Where setting name can be any of: "];
        lines = [@lines, valid.keys().to_english()];
        $parse_lib.tell_error(lines, syntax);
    }
    args = $parse_lib.options(args, #[["public", [1, 0]]]);
    public = args[2];
    value = args[1].to_string();
    .set_data(what, value, public["public"][1]);
.

public data_cmd
    arg cmdstr, com, who;
    var valid, line, user, out;
    
    if (!who)
        user = this();
    else
        user = (| .find_object(who, 'user, 'environment, 'grasp) |);
    if (!user) {
        .tell("!  No user found by the name \"" + who + "\".");
        .tell("!  To set a value use the syntax:");
        .tell("!    '" + com + " [+p|+public|-p|-public] my <parameter> is|are  <value>'");
        return 0;
    }
    out = "Current personal data settings on " + user.namef() + ":";
    
    //  valid = $user_data.valid_settings();
    out = [out, @user.display_data()];
    if (user.connected())
        out = [@out, user.name() + " is connected."];
    else
        out = [@out, user.name() + " was last connected at " + $time.ltime(abs(user.connected_at())) + " " + $time.ldate(abs(user.connected_at()))];
    return out;
.

public display_data
    arg [dont_display];
    var x, valid, line, len, pub, actor, data, lines, output;
    
    actor = sender();
    len = actor.linelen();
    valid = $user_data.valid_settings();
    output = [];
    for x in (valid) {
        if (!(x[2] in dont_display)) {
            data = (| user_data[x[2]] |);
            if (data) {
                if (data[1] || .is_writable_by(actor))
                    line = data[2];
                else
                    line = "not public.";
            } else {
                continue;
            }
            pub = (| user_data[x[2]][1] |);
            pub = !pub && type(pub) != 'error ? "*" | " ";
            if (len - 20 < line.length()) {
                output = [@output, pub + $string.capitalize(x[1]) + ":"];
                lines = $string.wrap_line(line, len, "   ");
                lines = lines.replace(1, "   " + lines[1]);
                output = output + lines;
            } else {
                output = output + [pub + ($string.capitalize(x[1]) + ": ").pad(13) + line];
            }
        }
    }
    return output;
.

public set_info_cmd
    arg cmdstr, cmd, what, is, args;
    var valid, lines, syntax, value, public;
    
    (> .perms(sender(), 'this) <);
    valid = $user_data.valid_settings();
    what = (| valid[what] |);
    if (!what) {
        syntax = cmd + " <setting name> is|to <value> [+/-public]";
        lines = ["Where setting name can be any of: "];
        lines = [@lines, valid.keys().to_english()];
        $parse_lib.tell_error(lines, syntax);
    }
    args = $parse_lib.options(args, #[["public", [1, 0]]]);
    public = args[2];
    value = args[1].to_string();
    if (value in ["none", "<none>"])
        value = "";
    .set_data(what, value, public["public"][1]);
    if (!value)
        .tell("You unset " + tostr(what).capitalize() + ".");
    else
        .tell("You set " + tostr(what).capitalize() + " to " + $string.chop(value, 50));
.

public user_data
    arg [which];
    var d, v;
    
    if (!which && !.is_writable_by(sender()))
        throw(~perm, "You are unable to get full user data, try specific fields.");
    if (which) {
        which = which[1];
        v = $user_data.valid_settings().values();
        d = (| user_data[which] |);
        if (!d && !(which in v))
            throw(~type, "No user data field type of " + toliteral(which));
        else if (!d)
            return "Not set.";
        else if (!d[1] && !.is_writable_by(sender()))
            throw(~perm, "That value is not public.");
        else
            return d[2];
    } else {
        return user_data;
    }
.



parent $help_root
parent $user_interfaces
object $help_ui

var $root fertile 1
var $root inited 1
var $root objname 'help_ui
var $root created_on 796268969
var $root flags ['parameters, 'methods, 'code, 'fertile, 'core]
var $has_commands local #[["@help", [["@help", "*", "@help <any>", 'help_cmd, #[[1, ['any, []]]]]]], ["@help-link", [["@help-link", "*", "@help-link <any>", 'help_link_cmd, #[[1, ['any, []]]]]]]]
var $has_commands shortcuts #[["?*", ['help_cmd, ["?", 1]]]]
var $command_cache shortcut_cache 0
var $command_cache remote_cache 0
var $command_cache local_cache 0
var $help_ui current 1
var $help_ui history [$help_summary]
var $help_ui indices [$help_index_root]

protected help_indices
    return indices;
.

root init_help_ui
    history = [$help_lib.default_node()];
    indices = [$help_lib.default_index()];
    current = 1;
.

public current_node
    return history[current];
.

protected help_cmd
    arg cmdstr, cmd, args;
    var o, opt, optval, way, node;
    
    o = #[["?", ["h?istory"]], ["<", ["b?ack"]], [">", ["f?orward"]], [".", ["fixate"]]];
    args = $parse_lib.getopt(args, o.values());
    opt = args[2];
    args = args[1];
    if (!opt) {
        if (!args) {
            if (cmd == "?")
                node = .current_node();
            else
                node = $help_lib.default_node();
        } else {
            args = args.to_string();
            if (args[1] in o.keys()) {
                opt = o[args[1]][1];
                if (args.length() > 1)
                    optval = args.subrange(2);
            } else {
                catch ~nonode {
                    node = (> $help_lib.parse_reference(args) <);
                } with handler {
                    return traceback()[1][2];
                }
            }
        }
    } else {
        // since all options override each other, just use the last one.
        optval = opt[opt.length()][4];
        opt = opt[opt.length()][1];
    }
    if (!node) {
        catch ~nonode {
            switch (opt) {
                case "h?istory":
                    return ._help_node_history();
                case "b?ack":
                    node = (> ._navigate_node_history(optval, 'back) <);
                case "f?orward":
                    node = (> ._navigate_node_history(optval, 'forward) <);
                case "fixate":
                    if (current != history.length()) {
                        node = history[current];
                        history = (| history.delete(current) |) || history;
                        history = history + [node];
                        current = history.length();
                    }
                    return;
            }
        } with handler {
            return traceback()[1][2];
        }
    }
    .set_help_node(node);
    .tell_help_node(node);
.

protected last_visited
    (> .perms(sender()) <);
    return last_visited;
.

protected _find_help_node
    arg what, type;
    var pos, cnode;
    
    (> .perms(sender(), 'this) <);
    switch (type) {
        case 'index:
            cnode = $help_index.find_help_node(what);
            if (cnode)
                return cnode;
            else
                throw(~nonode, "Unable to find help on \"" + what + "\".");
        case 'link:
            if (what in current_node.links().keys())
                return (current_node.links())[what];
            else
                throw(~nonode, "No node link \"" + what + "\" found on current node.");
        default:
            if (current_node.(type)()) {
                if (what.length() == 1) {
                    return current_node.(type)()[1];
                } else {
                    what = what.subrange(2);
                    pos = what in current_node.(type)().map('name);
                    if (pos)
                        return current_node.(type)()[pos];
                    else
                        throw(~nonode, "No downnode named " + what);
                }
            } else {
                throw(~nonode, "No " + tostr(type) + " defined from node " + current_node.name() + ".");
            }
    }
.

protected _back_help_node
    var pos;
    
    (> .perms(sender(), 'this) <);
    pos = current - 1;
    if (pos >= 1) {
        current = pos;
        return history[current];
    }
    throw(~nonode, "You are at the start of your help node history, use \"??\" to list the history.");
.

protected _forward_help_node
    var pos;
    
    (> .perms(sender(), 'this) <);
    pos = current + 1;
    if (pos <= $help_lib.history_cap() && pos <= history.length()) {
        current = pos;
        return history[current];
    }
    throw(~nonode, "You are at the end of your help node history, use \"??\" to list the history.");
.

protected set_help_node
    arg node;
    
    if (node in history && history[current] == node)
        return;
    while (history.length() >= $help_lib.history_cap())
        history = history.delete(1);
    history = history + [node];
    current = history.length();
.

protected _navigate_node_history
    arg what, way;
    var node;
    
    (> .perms(sender(), 'this) <);
    if (!what) {
        return (> .(tosym("_" + tostr(way) + "_help_node"))() <);
    } else {
        for node in (history) {
            if (node.match_name(what))
                return node;
        }
        throw(~nonode, "There is no node \"" + what + "\" in your history.");
    }
.

protected _help_node_history
    var node, line;
    
    .tell("Help node history:");
    for node in [1 .. history.length()] {
        line = "   ";
        if (node == current)
            line = "=> ";
        catch any {
            .tell(line + history[node].name());
        } with handler {
            history = history.delete(node);
            .set_help_node(history[1]);
            .tell(line + ">> ERROR: INVALID NODE IN HISTORY <<");
        }
    }
.

protected tell_help_node
    arg node;
    var out, len, clen, line, n;
    
    len = .linelen() % 2 ? .linelen() - 1 | .linelen();
    .tell((" " + node.node_name() + " ").center(len, "-"));
    .ptell(node.body(), #[['type, 'help], ['ctype, 'ctext]]);
    .tell("".pad(len, "-"));
.

root uninit_help_ui
    .clear_variable('history);
    .clear_variable('indices);
    .clear_variable('current);
.

protected help_node_history
    return history;
.



parent $user_interfaces
object $messages_ui

var $root fertile 1
var $root inited 1
var $root objname 'messages_ui
var $root created_on 796268969
var $root flags ['parameters, 'methods, 'code, 'fertile, 'core]
var $has_commands local #[["@mes?sages|@mesg", [["@mes?sages|@mesg", "*", "@mes?sages|@mesg <any>", 'messages_cmd, #[[1, ['any, []]]]]]]]
var $command_cache shortcut_cache 0
var $command_cache remote_cache 0
var $command_cache local_cache 0

public messages_cmd
    arg cmdstr, cmd, args;
    var opt, definer, t, p, name, value, object, reg, s;
    
    if (!args)
        return .display_messages("", this());
    reg = "(.*)<(.*)>(.*)".sub(args);
    if (reg) {
        definer = .match_env_nice(reg[2]);
        args = reg[1] + reg[2];
    }
    if ("=" in args)
        s = "=";
    else
        s = " ";
    if (s in args) {
        name = args.subrange(1, (s in args) - 1).trim();
        args = args.subrange((s in args) + 1);
    } else {
        name = args;
        args = "";
    }
    if (":" in name) {
        object = .match_env_nice(name.subrange(1, (":" in name) - 1));
        name = name.subrange((":" in name) + 1);
    } else {
        object = this();
    }
    if (!object.has_ancestor($has_messages))
        return object.namef() + " cannot have any messages.";
    if (!name)
        return .display_messages("", object);
    value = args.unquote();
    catch any {
        object.set_message(name, value);
        .tell("Message changed to:");
        .display_messages(name, object);
    } with handler {
        return traceback()[1][2];
    }
.

public display_messages
    arg mask, obj;
    var output, definer, message, p, s, uncompiler, name, messages;
    
    output = [];
    messages = obj.messages();
    for definer in (messages.keys()) {
        output = [@output, definer.namef('xref) + ":"];
        for message in (messages[definer].keys()) {
            name = (definer._find_message_definer(message))[1];
            uncompiler = definer.message_info(name, 'uncompiler);
            p = uncompiler.eval_ctext(obj.message(message)._ctext());
            if (type(p) == 'dictionary) {
                for s in (p.keys()) {
                    if ($string.match_begin(message + "." + s, mask))
                        output = [@output, ["  " + message + "." + s + " = "].join(p[s])];
                }
            } else if ($string.match_begin(message, mask)) {
                output = [@output, ["  " + message + " = "].join(p)];
            }
        }
    }
    return output;
.



parent $user_interfaces
object $settings_ui

var $root inited 1
var $root objname 'settings_ui
var $root created_on 796268969
var $root flags ['parameters, 'methods, 'code, 'core]
var $has_commands local #[["@set?ings", [["@set?ings", "*", "@set?ings <any>", 'set_cmd, #[[1, ['any, []]]]]]], ["@set?tings", [["@set?tings", "on|from *", "@set?tings on|from <object reference>", 'settings_on_cmd, #[[2, ['objref, []]]]]]]]
var $command_cache shortcut_cache 0
var $command_cache remote_cache 0
var $command_cache local_cache 0

public settings_cmd
    arg cmdstr, cmd, args;
    var flag, value, template, syn, bool, line;
    
    (> .perms(sender()) <);
    syn = cmd + " [+|-]<flag>[=<value>]";
    if (!args) {
        return .list_settings('local);
    } else if (args in ["-all", "-a"]) {
        return .list_settings('all);
    } else {
        bool = args[1] in ["-", "+"];
        if (bool)
            args = args.subrange(2);
        args = args.explode("=");
        flag = args[1];
        if (args.length() == 2)
            value = args[2];
        else
            value = "";
        template = .setting_template(flag);
        if (!template)
            $parse_lib.tell_error("No setting available with the flag \"" + flag + "\".", syn);
        switch (template[2]) {
            case 'boolean:
                if (!bool)
                    $parse_lib.tell_error("Value must be boolean (+|-" + flag + ").", syn);
                value = bool - 1;
            case 'integer:
                if (!$string.is_numeric(value))
                    $parse_lib.tell_error("Value must be an integer (" + flag + "=<integer>).", syn);
                value = toint(value);
            case 'string:
                if (!value)
                    $parse_lib.tell_error("Value must be a string (" + flag + "=<string>).", syn);
        }
        .set_setting(flag, value);
        line = "Setting " + flag + " set to ";
        switch (template[2]) {
            case 'boolean:
                line = line + (value == 1 ? "+" | "-");
            default:
                line = line + toliteral(value);
        }
        .tell(line);
    }
.

public settings_on_cmd
    arg cmdstr, cmd, prep, ref;
    
    (> .perms(sender()) <);
    
    // this is a hookneyed way to do it, and wont work out in the long run,
    // but until we get arguments in the parser this will work fine
    if (ref[2] == ref[3])
        return ._show_settings(ref[3]);
    else
        return ._show_settings_on(ref[3], ref[2]);
.

public _show_setting
    arg setting, definer, object;
    var line;
    
    line = "  " + setting + " = ";
    setting = (| object.display_setting(setting, definer) |);
    if (setting != ~setting)
        line = line + setting;
    return line;
.

public _show_settings_on
    arg definer, object;
    var settings, s, setting, line, out;
    
    if (!object.trusts(this()))
        return [definer.namef('xref) + ":", "  ** Unable to see settings **"];
    settings = (| definer.defined_settings() |) || [];
    out = [];
    for s in (settings)
        out = [@out, ._show_setting(s, definer, object)];
    if (!out)
        out = ["  (none)"];
    return [definer.namef('xref) + ":"] + out;
.

public set_cmd
    arg cmdstr, cmd, args;
    var opt, definer, t, p, name, value, object, reg, s;
    
    (> .perms(sender()) <);
    if (!args)
        return ._show_settings(this());
    reg = $string.match_regexp("<.*>", args);
    if (reg) {
        definer = .match_env_nice(args.subrange(reg[1][1] + 1, reg[1][2] - 2));
        args = args.subrange(1, reg[1][1] - 1) + args.subrange(reg[1][1] + reg[1][2]);
    }
    if ("=" in args)
        s = "=";
    else
        s = " ";
    if (s in args) {
        name = args.subrange(1, (s in args) - 1).trim();
        args = args.subrange((s in args) + 1);
    } else {
        name = args;
        args = "";
    }
    if (":" in name) {
        object = .match_env_nice(name.subrange(1, (":" in name) - 1));
        name = name.subrange((":" in name) + 1);
    } else {
        object = this();
    }
    if (!name)
        return ._show_settings(object);
    
    // this should fix the quotes
    value = args.unquote();
    catch any {
        ._change_setting(name, value, definer, object);
        .tell(["Setting changed to:", ._show_setting(name, definer, object)]);
    } with handler {
        return traceback()[1][2];
    }
.

public _show_settings
    arg object;
    var a, s, out;
    
    if (!object.trusts(this()))
        return object.namef('ref) + " does not trust you enough to show you " + ((| object.gender() |) || $gender_neuter).pronoun('pp) + " settings.";
    out = [];
    for a in (object.ancestors()) {
        if (a == definer())
            break;
        s = (| a.defined_settings() |);
        if (s)
            out = out + ._show_settings_on(a, object);
    }
    return out;
.

public _change_setting
    arg name, value, definer, object;
    var current, style, index, pos;
    
    (> .perms(sender(), 'writers) <);
    style = name.last();
    if (style in ["+", "-"]) {
        name = name.subrange(1, name.length() - 1);
        if (!definer)
            definer = object._find_setting_definer(name);
        current = object.setting(name, definer) || [];
        if (style == "+")
            value = [@current || [], value];
        else
            value = current.delete(toint(value));
    }
    catch ~check, ~set {
        (> object.set_setting(definer, name, value) <);
    } with handler {
        .tell(traceback()[1][2]);
        throw(~stop, "", 'no_traceback);
    }
.



parent $body
parent $interaction
parent $mail_ui
parent $command_aliases
parent $bad_commands
parent $user_data
parent $help_ui
parent $messages_ui
parent $has_messages
parent $settings_ui
object $user

var $root owners [$user]
var $root inited 1
var $root created_on 796268969
var $root owned [$user]
var $root manager $user
var $root objname 'user
var $root flags ['parameters, 'methods, 'code, 'core, 'fertile]
var $root managed [$user]
var $root child_index 2
var $messaged verbs #[]
var $location contents []
var $located location $nowhere
var $located obvious 1
var $user password "*"
var $user connected_at 0
var $user last_command_at 0
var $user connections []
var $user linelen 0
var $user title 0
var $user action 0
var $user channels []
var $user pagelen 0
var $user activity 0
var $user parsers [$command_parser]
var $user filters []
var $user tell_traceback 0
var $user context #[]
var $user remembered 0
var $user evaluator 0
var $user connected_seconds 0
var $command_aliases command_aliases []
var $mail_list letters #[]
var $mail_list letters_index #[]
var $mail_list senders 1
var $mail_list readers []
var $mail_list notify [$user]
var $mail_list last_letter 0
var $mail_list mail []
var $mail_ui subscribed #[[$user, 791485891]]
var $mail_ui current #[['location, 0], ['list, $user]]
var $gendered gender $gender_neuter
var $described prose []
var $named name ['prop, "Generic User Object", "Generic User Object"]
var $named name_aliases []
var $user_data user_data #[['real_name, [1, "???"]], ['email, [1, "???"]]]
var $has_commands local #[["@quit", [["@quit", "", "@quit", 'quit_cmd, #[]]]], ["i?nventory", [["i?nventory", "", "i?nventory", 'inventory_cmd, #[]]]], ["@title", [["@title", "*", "@title <any>", 'title_cmd, #[[1, ['any, []]]]]]], ["@add-name-alias|@ana", [["@add-name-alias|@ana", "*", "@add-name-alias|@ana <any>", 'add_name_alias_cmd, #[[1, ['any, []]]]]]], ["@del-name-alias|@dna", [["@del-name-alias|@dna", "*", "@del-name-alias|@dna <any>", 'del_name_alias_cmd, #[[1, ['any, []]]]]]], ["@audit", [["@audit", "*", "@audit <any>", 'audit_cmd, #[[1, ['any, []]]]]]], ["@who", [["@who", "*", "@who <any>", 'who_cmd, #[[1, ['any, []]]]]]], ["@del-command-a?lias|@dca?lias", [["@del-command-a?lias|@dca?lias", "*", "@del-command-a?lias|@dca?lias <any>", 'del_command_alias_cmd, #[[1, ['any, []]]]]]], ["@command-a?liases|@ca?liases", [["@command-a?liases|@ca?liases", "*", "@command-a?liases|@ca?liases <any>", 'command_aliases_cmd, #[[1, ['any, []]]]]]], ["@wrap", [["@wrap", "*", "@wrap <any>", 'wrap_cmd, #[[1, ['any, []]]]]]], ["@add-command-a?lias|@aca?lias", [["@add-command-a?lias|@aca?lias", "*", "@add-command-a?lias|@aca?lias <any>", 'add_command_alias_cmd, #[[1, ['any, []]]]]]], ["@com?mands", [["@com?mands", "*", "@com?mands <any>", 'commands_cmd, #[[1, ['any, []]]]]]], ["@spawn", [["@spawn", "*", "@spawn <any>", 'spawn_cmd, #[[1, ['any, []]]]]]], ["@rehash?-commands", [["@rehash?-commands", "", "@rehash?-commands", 'rehash_cmd, #[]]]], ["@news", [["@news", "", "@news", 'news_cmd, #[]]]], ["@forget", [["@forget", "*", "@forget <any>", 'forget_cmd, #[[1, ['any, []]]]]]], ["@whereis|@where-is", [["@whereis|@where-is", "*", "@whereis|@where-is <any>", 'whereis_cmd, #[[1, ['any, []]]]]]], ["@ways", [["@ways", "", "@ways", 'ways_cmd, #[]]]], ["@password|@passwd", [["@password|@passwd", "*", "@password|@passwd <any>", 'password_cmd, #[[1, ['any, []]]]]]], ["@describe|@prose", [["@describe|@prose", "*", "@describe|@prose <any>", 'description_cmd, #[[1, ['any, []]]]]]], ["l?ook", [["l?ook", "", "l?ook", 'look_cmd, #[]]]], ["@age", [["@age", "*", "@age <object>", 'age_cmd, #[[1, ['object, []]]]]]], ["@context", [["@context", "", "@context", 'context_cmd, #[]]]], ["get|take", [["get|take", "*", "get|take <thing>", 'get_cmd, #[[1, ['descendant, [$thing]]]]], ["get|take", "* from *", "get|take <any> from <thing>", 'get_from_cmd, #[[1, ['any, []]], [3, ['descendant, [$thing]]]]]]], ["drop", [["drop", "*", "drop <thing>", 'drop_cmd, #[[1, ['descendant, [$thing]]]]]]], ["give|put", [["give|put", "* to|in *", "give|put <thing> to|in <thing>", 'give_to_cmd, #[[1, ['descendant, [$thing]]], [3, ['descendant, [$thing]]]]]]], ["@rename", [["@rename", "*", "@rename <any>", 'rename_cmd, #[[1, ['any, []]]]]]], ["@name-alias?es|@na", [["@name-alias?es|@na", "*", "@name-alias?es|@na <object>", 'name_aliases_cmd, #[[1, ['object, []]]]]]], ["go", [["go", "*", "go <any>", 'go_cmd, #[[1, ['any, []]]]]]], ["@status|@uptime", [["@status|@uptime", "", "@status|@uptime", 'status_cmd, #[]]]], ["@add-writer|@aw", [["@add-writer|@aw", "*", "@add-writer|@aw <any>", 'add_writer_cmd, #[[1, ['any, []]]]]]], ["@del-writer|@dw", [["@del-writer|@dw", "*", "@del-writer|@dw <any>", 'del_writer_cmd, #[[1, ['any, []]]]]]], ["@trusted?-by", [["@trusted?-by", "*", "@trusted?-by <any>", 'trusted_by_cmd, #[[1, ['any, []]]]]]], ["@add-trust?ee|@at", [["@add-trust?ee|@at", "*", "@add-trust?ee|@at <any>", 'add_trustee_cmd, #[[1, ['any, []]]]]]], ["@del-trust?ee|@dt", [["@del-trust?ee|@dt", "*", "@del-trust?ee|@dt <any>", 'del_trustee_cmd, #[[1, ['any, []]]]]]], ["@writes", [["@writes", "*", "@writes <any>", 'writes_cmd, #[[1, ['any, []]]]]]], ["@chman?age", [["@chman?age", "*", "@chman?age <any>", 'chmanage_cmd, #[[1, ['any, []]]]]]], ["@manage?d", [["@manage?d", "*", "@manage?d <any>", 'managed_cmd, #[[1, ['any, []]]]]]], ["@owned", [["@owned", "*", "@owned <any>", 'owned_cmd, #[[1, ['any, []]]]]]], ["@remember", [["@remember", "* as *", "@remember <object> as <any>", 'remember_cmd, #[[1, ['object, []]], [3, ['any, []]]]]]], ["@remembered", [["@remembered", "*", "@remembered <any>", 'remembered_cmd, #[[1, ['any, []]]]]]]]
var $has_commands shortcuts #[]
var $has_settings settings #[]
var $has_settings setting_types #[["terminated-tell", #[['type, "boolean"], ['check, 'is_boolean], ['get, 'get_direct_setting], ['set, 'set_direct_setting], ['del, 'del_direct_setting], ['set_args, ['name, 'definer, 'value, 'style]], ['get_args, ['name, 'definer]]]], ["content-type", #[['type, "string"], ['check, 'is_anything], ['get, 'get_direct_setting], ['set, 'set_direct_setting], ['del, 'del_direct_setting], ['set_args, ['name, 'definer, 'value, 'style]], ['get_args, ['name, 'definer]]]]]
var $has_settings setting_data #[[$user, #[["content-type", "text/plain"]]]]
var $has_settings defined_settings #[["content-type", #[['get, 'get_local_setting], ['set, 'set_content_type], ['check, 'check_content_type], ['del, 'delete_local_setting], ['check_args, []], ['get_args, []], ['set_args, []]]], ["home-page", #[['get, 'get_local_setting], ['set, 'set_local_setting], ['check, 'is_any], ['del, 'delete_local_setting], ['check_args, []], ['get_args, []], ['set_args, []]]], ["terminated-tell", #[['get, 'get_local_setting], ['set, 'set_local_setting], ['check, 'is_boolean], ['del, 'delete_local_setting], ['check_args, []], ['get_args, []], ['set_args, []], ['display, 'display_boolean_yes_no]]], ["experienced", #[['get, 'get_local_setting], ['set, 'set_local_setting], ['check, 'is_boolean], ['del, 'delete_local_setting], ['check_args, []], ['get_args, []], ['set_args, []], ['display, 'display_boolean_yes_no]]]]
var $has_settings local_settings #[]
var $help_ui indices [$help_index_root]
var $help_ui history [$help_summary]
var $help_ui current 1
var $command_cache shortcut_cache 0
var $command_cache remote_cache 0
var $command_cache local_cache 0

root init_user
    password = "*";
    connected_at = 0;
    last_command_at = 0;
    connections = [];
    parsers = [$command_parser];
    action = "";
    context = #[];
    .set_quota($sys.get_starting('quota));
    $user_db.insert(.name(), this());
    .set_flags([]);
    .move_to($body_cave);
.

root uninit_user
    var conn;
    
    if (.connected())
        (| .location().did_disconnect() |);
    
    // and incase any are lying about
    for conn in (connections)
        (| conn.user_going_away() |);
    password = 0;
    connections = 0;
    (| $user_db.remove(.name()) |);
.

public will_move
    arg mover, place;
    
    (> pass(mover, place) <);
    if (!place.has_ancestor($place))
        throw(~user, "Users can only move into descendants of $place.");
.

public connected_at
    return connected_at;
.

public last_command_at
    return last_command_at;
.

public tell
    arg what, [args];
    var filter, ctype;
    
    // first argument should always be the content type, subsequent
    // arguments can relate to the content type.
    if (!.connected())
        return;
    ctype = [@args, 'interaction][1];
    switch (ctype) {
        case 'channel:
            if (!.listens_to_channel(@args.subrange(2)))
                return;
    }
    ._tell(what);
.

public set_password
    arg str;
    var x, num;
    
    .perms(sender(), 'manager);
    if (str.length() < 5)
        throw(~badpasswd, "Passwords must be at least 5 characters long.");
    
    // this is assuming they have alphabetic characters as well (shrug).
    // for x in [1 .. str.length()] {
    //   if (str[x] in "1234567890")
    //     num = num + 1;
    // }
    // if (num < 2)
    //   throw(~badpasswd, "Passwords must contain at least 2 numeric characters.");
    password = str.crypt();
.

public check_password
    arg str;
    
    return str.crypt(password.subrange(1, 2)) == password;
.

public did_move
    arg [args];
    var loc;
    
    (> pass(@args) <);
    loc = .location();
    (| loc.look_cmd("look") |);
.

public parsers
    .perms(sender(), 'trusts);
    return parsers;
.

public add_parser
    arg parser, [position];
    
    // adds a new $parse_libr at 'position.
    (> .perms(sender(), 'this) <);
    
    // do this in three steps, first make sure the posistion is valid,
    // then check for it already, then figure its insert position.
    position = [@position, 'first][1];
    if (!(position in ['last, 'first]))
        throw(~type, "Posistion types must be one of: 'last, 'first.");
    
    // does it exist?  If so remove it.
    if (parser in parsers)
        parsers = parsers.setremove(parser);
    
    // figure position
    if (position == 'last)
        position = parsers.length() + 1;
    else if (position == 'first)
        position = 1;
    parsers = parsers.insert(position, parser);
.

public del_parser
    arg parser;
    var keepers;
    
    // removes a parser.  Cannot remove $command_parser (put things in
    // front of it instead).
    if (parser == $command_parser)
        throw(~no, "You must always have the $command_parser.");
    parsers = parsers.setremove(parser);
.

public parse_line
    arg line;
    var result, c, rval;
    
    (> .perms(caller(), $connection) <);
    last_command_at = time();
    catch any {
        result = parsers ? parsers[1].parse(this(), line, @parsers.subrange(2), $null_parser) | 'failed;
        if (type(result) == 'list) {
            rval = (> result[1].(result[2])(@result.subrange(3)) <);
            switch (type(rval)) {
                case 'list, 'frob, 'string:
                    return .ptell(rval, #[['type, 'parser], ['command, result[2]]]);
                default:
                    return rval;
            }
        }
        switch (result) {
            case 'failed:
                // try one last time to at least give them an intelligent
                // answer...
                for c in ($places.coordinate_shortcuts().keys()) {
                    if ($string.match_template(c, line))
                        return .tell("There is no exit " + line + " here.");
                }
                .tell("I don't understand \"" + $string.chop(line, .linelen() - 22) + "\".");
            case 'ok:
            default:
                .tell(tostr(result));
        }
    } with handler {
        if (traceback()[1][3] != 'no_traceback)
            .tell_traceback(traceback(), line, 0, error());
    }
.

public login
    arg connection;
    var loc;
    
        if (sender() != this() || definer() != caller())
            throw(~perm, "Invalid access to private method.");
        (| .reset_parsers() |);
        $user_db.did_connect();
        connected_at = time();
        last_command_at = time();
        loc = .location();
        if (loc == $body_cave) {
            if (.home() != $body_cave)
                (| .move_to(.home()) |);
            else
                (| .move_to($places.place('starting)) |);
        }
        (| .location().did_connect() |);
        (| .rehash_caches() |);
        (| .login_notify(connection) |);
        $channels.announce('login, .namef('titled) + " has connected (total: " + tostr($user_db.total_connected()) + ")", 0, [this()], this());
.

public login_again
    arg connection;
    
    if (sender() != this() || definer() != caller())
        throw(~perm, "Invalid access to private method.");
    last_command_at = time();
    (| .tell($string.center("* * * " + $integer.n_to_nth(connection in .connections()) + " Login successful (" + connection.addr() + ") * * *", .linelen())) |) || (| .tell($string.center("* * * " + $integer.n_to_nth(connection in .connections()) + " Login successful (" + connection.address() + ") * * *", .linelen())) |);
.

public logout
    arg connection;
    
    if (sender() != this() || definer() != caller())
        throw(~perm, "Invalid access to private method.");
    (| .location().did_disconnect() |);
    (| .reset_parsers() |);
    (| $user_db.did_disconnect() |);
    
    // Track how long they are online (random info) -Lynx
    connected_seconds = connected_seconds + (time() - connected_at);
    
    // set this to -last_command so we know they aren't connected
    // (and using last command will be last_login)
    connected_at = -last_command_at;
    
    // user specific things
    if (!($guest in .parents())) {
        (| $housekeeper.did_disconnect() |);
        (| $user_db.last_log_disconnect(this()) |);
    } else {
        (| $user_db.last_log_disconnect($guest) |);
    }
    (| .purge_caches() |);
    $channels.announce('login, .namef('titled) + " has disconnected (total: " + tostr($user_db.total_connected()) + ")", 0, [this()], this());
.

public connections
    return connections;
.

public connected
    return connections ? 1 | 0;
.

protected who_cmd
    arg cmdstr, com, [line];
    var who, opts, opt, args, all;
    
    // just the basic who listing
    who = [];
    if (!line[1]) {
        args = [$user_db.connected(), "Connected Users"];
    } else if (line[1][1] == "@") {
        args = ._who_at_place(line[1].subrange(2));
    } else {
        args = $parse_lib.getopt(line[1], [["a?ll"], ["p?rogrammers"], ["a?dmins"], ["s?hort"]]);
        opts = args[2];
        args = args[1];
        if (opt = "a?ll" in opts.slice(1))
            all = opts[opt][3];
        if (opt = "p?rogrammers" in opts.slice(1))
            args = ._who_programmers(args, all);
        else if (opt = "a?dmins" in opts.slice(1))
            args = ._who_admins(args, all);
        else if (opt = "s?hort" in opts.slice(1))
            return ._who_short();
        else
            args = ._who_is(@line);
    }
    if (!args)
        return "Ack, nobody to list?";
    .tell($code_lib.generate_listing(@args));
.

protected quit_cmd
    arg [args];
    
    return 'disconnect;
.

protected inventory_cmd
    arg [args];
    var i;
    
    if (.contents()) {
        .tell("Carrying:");
        for i in (.contents())
            .tell(" " + i.namef());
    } else {
        .tell("You are empty-handed.");
    }
.

public match_env_nice
    arg name, [syntax];
    var obj, args, line;
    
    // calls .match_environment() returns nice errors. as well as stopping if it
    // breaks.  No returns neccessary
    syntax = [@syntax, ""][1];
    catch any {
        obj = .match_environment(name);
    } with handler {
        switch (error()) {
            case ~ambig:
                args = traceback()[1][3];
                line = "\"" + args[args.length()] + "\" can match any of: ";
                line = line + $list.map_to_english(args[1], 'namef);
                (> .tell_error(syntax, line) <);
            case ~objnf:
                line = "Nothing found by the name \"" + traceback()[1][3] + "\".";
                (> .tell_error(syntax, line) <);
            default:
                line = traceback()[1][2];
                (> .tell_error(syntax, line) <);
        }
    }
    return obj;
.

public linelen
    return linelen || 79;
.

public idle_seconds
    return time() - last_command_at;
.

public title
    return title || "";
.

public action
    // different from activity, returns a more accurate second to second action
    if (.connected())
        return action || "";
    else
        return "(asleep)";
.

protected title_cmd
    arg cmdstr, com, str;
    
    catch any {
        .set_title(str);
        .tell("Title Set as: \"" + str + "\"");
    } with handler {
        $parse_lib.tell_error(traceback()[1][2]);
    }
.

protected commands_cmd
    arg cmdstr, cmd, args;
    var s, full, o, opt, opts, lcmds, rcmds, len, obj, shorts, m, c, local, remote, short, all;
    
    s = cmd + " [options] <object>";
    opts = [["f?ull"], ["a?ll"], ["l?ocal"], ["r?emote"], ["s?hortcuts"]];
    args = $parse_lib.getopt(args, opts);
    o = args[2];
    local = remote = short = 1;
    if (opt = "f?ull" in o.slice(1))
        full = o[opt][3];
    if (opt = "a?ll" in o.slice(1))
        all = o[opt][3];
    if (opt = "r?emote" in o.slice(1))
        remote = o[opt][3];
    if (opt = "s?hortcuts" in o.slice(1))
        short = o[opt][3];
    if (opt = "l?ocal" in o.slice(1))
        local = o[opt][3];
    args = args[1].to_string();
    if (!args && !all) {
        (| .tell_error(cmd + " [options] <object>") |);
        return "!  Defaults: -f?ull -a?ll +l?ocal +r?emote +s?hortcuts";
    } else {
        args = args || "me";
        obj = .match_env_nice(args);
    }
    lcmds = rcmds = #[];
    shorts = [];
    if (all) {
        if (local)
            lcmds = obj.all_local_commands();
        if (remote)
            rcmds = obj.all_remote_commands();
        if (short)
            shorts = obj.all_shortcuts();
    } else {
        if (local) {
            c = obj.local_commands();
            if (c)
                lcmds = #[[obj, c]];
        }
        if (remote) {
            c = obj.remote_commands();
            if (c)
                rcmds = #[[obj, c]];
        }
        if (short)
            shorts = obj.shortcuts().to_list();
    }
    if (full) {
        m = "long";
        len = .linelen() / 3 * 2;
    } else {
        m = "short";
        len = .linelen();
    }
    o = [];
    if (lcmds)
        o = o + $command_lib.(tosym("format_commands_" + m))(lcmds, "Local", len);
    if (rcmds)
        o = o + $command_lib.(tosym("format_commands_" + m))(rcmds, "Remote", len);
    if (shorts) {
        o = o + ["Shortcuts:"];
        for c in (shorts)
            o = o + ["  " + $command_lib.unparse_shortcut(c)];
    }
    return o || "No commands defined on " + obj.namef('ref);
.

public pagelen
    return pagelen || 24;
.

public set_pagelen
    arg len;
    
    if (!.is_writable_by(sender()))
        throw(~perm, "Sender not an owner.");
    if (type(len) != 'integer)
        throw(~type, "pagelength must be an integer");
    pagelen = len;
.

public set_linelen
    arg len;
    
    if (!.is_writable_by(sender()))
        throw(~perm, "Sender not an owner.");
    if (type(len) != 'integer)
        throw(~type, "Linelength must be an integer");
    linelen = len;
.

protected name_aliases_cmd
    arg cmdstr, com, obj;
    
    if (!obj.is($named))
        return obj.name() + " is not descended from $named!";
    return "Name aliases for " + obj.name() + ": " + obj.name_aliases().to_english("none");
.

public activity
    var idle;
    
    // different from action, returns a broader version of your doings
    if (!.connected())
        return "asleep";
    if (activity)
        return activity;
    idle = .idle_seconds();
    if (idle < 180)
        return "";
    if (idle < 300)
        return "daydreaming";
    if (idle < 900)
        return "zoned";
    else
        return "long gone";
.

protected add_name_alias_cmd
    arg cmdstr, com, args;
    var syn, obj, alias;
    
    syn = com + " <alias> [to] <object>";
    args = args.explode_quoted();
    alias = (| args[1] |);
    if (!alias)
        return .tell_error("", syn);
    if (args.length() > 1 && args[2] == "to")
        args = args.delete(2);
    obj = .match_env_nice([@args, "me", "me"][2]);
    if (!obj)
        return "You must specify an object to add an alias to.";
    if (!obj.is($named))
        return obj.name() + " is not a descendant of $named!";
    if (alias in .name_aliases())
        return "You already have the name alias " + toliteral(alias);
    catch any {
        (> obj.add_name_alias(alias) <);
    } with handler {
        .tell_error(traceback()[1][2], syn);
    }
    return "Name Alias " + toliteral(alias) + " added to " + obj.name() + " (" + obj.name_aliases().to_english() + ").";
.

protected del_name_alias_cmd
    arg cmdstr, com, args;
    var syn, obj, alias;
    
    syn = com + " <alias> [from] <object>";
    args = args.explode_quoted();
    alias = (| args[1] |);
    if (!alias)
        return .tell_error("", syn);
    if (args.length() > 1 && args[2] == "from")
        args = args.delete(2);
    obj = .match_env_nice([@args, "me", "me"][2]);
    if (!obj)
        return "You must specify an object to delete an alias from.";
    if (!obj.is($named))
        return obj.name() + " is not a descendant of $named!";
    if (!(alias in .name_aliases()))
        return obj.name() + " does not have the name alias " + toliteral(alias);
    catch any {
        (> obj.del_name_alias(alias) <);
    } with handler {
        .tell_error(traceback()[1][2], syn);
    }
    return "Name Alias " + toliteral(alias) + " deleted from " + obj.name() + (obj.name_aliases() ? " (" + obj.name_aliases().to_english() + ")" | "");
.

public home
    arg [args];
    
    return (| .get_local_setting("home", $thing) |) || $body_cave;
.

protected rename_cmd
    arg cmdstr, cmd, args;
    var syn, object, name, aliases, article;
    
    syn = cmd + " <object> [to] <newname> [flags]";
    args = args.explode_quoted();
    if (!args || args.length() == 1)
        return .tell_error("", syn);
    object = .match_env_nice(args[1]);
    if (args[2] == "to")
        args = args.delete(2);
    if (args.length() == 1)
        return .tell_error("", syn);
    catch any {
        name = $code_lib.parse_name(args.subrange(2).to_string());
        aliases = name[2];
        article = name[1][2];
        name = name[1][1];
        if (name[1] == "$") {
            if (object.is($user) && !.is($admin))
                return "User objnames can only be changed by administrators.";
            name = tosym(name.subrange(2));
            (> object.set_objname(name) <);
            return "Sucessfully changed object name to " + object.objname() + ".";
        } else {
            if (!object.is($named))
                return .tell_error("Object \"" + object.objname() + "\" is not descended from $named.", syn);
            (> object.set_name(name, article) <);
            if (aliases)
                (> object.set_name_aliases(@aliases) <);
            return "Sucessfully changed " + (object == this() ? "your " | "") + "name [" + tostr(article) + "] to \"" + object.name() + "\"" + (object.name_aliases() ? " (" + object.name_aliases().to_english() + ")" | "");
        }
    } with handler {
        .tell_traceback(traceback());
        .tell_error(traceback()[1][2], syn);
    }
.

protected audit_cmd
    arg cmdstr, cmd, args;
    var owner, obj, col, str, out, total, line, syntax, loc, size, full, s, o;
    
    o = $parse_lib.getopt(args, [["f?ull"]]);
    args = o[1].to_string();
    full = (| "f?ull" in o[2].slice(1) |);
    if (full)
        full = o[2][full][3];
    owner = (| .match_environment(args) |);
    if (!owner) {
        owner = (| $user_db.find(args) |);
        if (!owner)
            return "Unable to find \"" + args + "\".";
    }
    if (!owner.owned())
        return owner.name() + " does not own anything.";
    if (full) {
        col = .linelen() / 2;
        out = [("Objects owned by " + owner.namef('ref) + ":").pad(col) + "bytes".pad(-10) + " Location"];
        for obj in (owner.owned()) {
            if (!valid(obj)) {
                .tell("  ** invalid object (" + toliteral(obj) + ") **");
                continue;
            }
            line = ("  " + obj.namef('xref)).pad(col);
            s = obj.size();
            size = s.to_english();
            line = line + size.pad(-(size.length() > 10 ? size.length() | 10)) + " ";
            loc = obj.has_ancestor($located) ? "[" + obj.location().name() + "]" | "";
            out = out + [(line + loc).pad(.linelen())];
            total = total + s;
        }
    } else {
        if (owner != this() && !.is($admin))
            return "Only admins can get quota usage information from other users.";
        out = ["Quota information on " + owner.namef('ref) + ":"];
        for obj in (owner.owned()) {
            if (valid(obj))
                total = total + obj.size();
        }
    }
    if (owner == this() || .is($admin)) {
        out = out + ["Total usage: " + $integer.to_english(total) + " bytes"];
        size = owner.get_quota();
        line = "Total quota: " + $integer.to_english(size) + " bytes";
        out = out + [line, "Remaining:   " + $integer.to_english(size - total) + " bytes"];
    }
    if (!owner.quota_valid()) {
        if (owner == this())
            out = out + ["*** You are over quota! ***"];
        else
            out = out + ["*** " + owner.name() + " is over quota! ***"];
    }
    return out;
.

protected login_notify
    arg connection;
    
    // Login Notifications -- called by .login()
    .tell(("*** Login successful (" + connection.address() + ") ***").center(.linelen()));
    .tell(("** Last connection at " + $time.date(abs(.connected_at())) + " **").center(.linelen()));
    .look_cmd();
    .tell("");
    if (.last_received_on() > (.subscribed())[this()])
        .tell("** You have new mail (use `@help mail` to learn about mail) **");
    .subscribe_cmd("", "", "-new");
.

protected news_cmd
    arg [args];
    var line, x, mail, m, base, length, out;
    
    mail = $mail_list_news.recent_mail();
    base = mail[1];
    mail = mail[2];
    length = mail.length();
    .new_list($mail_list_news);
    out = [$motd.server_name() + " news:", ""];
    for x in [1 .. length] {
        m = mail[x];
        out = out + [(m.has_read(this()) ? "       " | "NEW => ") + tostr(x + base).pad(-3) + ") " + m.subject()];
    }
    return out + ["", "Use \"@read #\", where # is the news item number, such as \"@read 1\".  All news items can be found on mail list *news.", "---"];
.

protected add_command_alias_cmd
    arg cmdstr, com, input;
    var syn, alias, actual, tmpalias, tmpactual, num;
    
    if (sender() != this())
        throw(~perm, "Sender is not this");
    input = input.explode("\"");
    
    // hell, i'll be nice and check for a few possibilities:
    syn = com + " \"<alias>\" [to] \"<actual command>\"";
    if (input.length() < 2)
        $parse_lib.tell_error("Not enough arguments (enclose each alias in quotes)", syn);
    if (input.length() > 3)
        $parse_lib.tell_error("Too many arguments (enclose each alias in quotes)", syn);
    
    // sort and get the alias and actual
    if (input.length() == 3)
        input = input.delete(2);
    alias = input[1];
    actual = input[2];
    
    // make sure the %foo's match up
    tmpalias = alias;
    tmpactual = actual;
    while ("%" in tmpalias) {
        num = tmpalias[("%" in tmpalias) + 1];
        if (!toint(num))
            $parse_lib.tell_error("referece cards must be integers", syn);
        num = "%" + num;
        if (!(num in tmpactual))
            $parse_lib.tell_error("reference cards to not match up", syn);
        tmpalias = tmpalias.replace(num, "");
        tmpactual = tmpactual.replace(num, "");
    }
    if ("%" in tmpactual)
        $parse_lib.tell_error("reference cards do not match up", syn);
    .add_command_alias(alias, actual);
    .tell("New command alias \"" + alias + "\" => \"" + actual + "\" added.");
.

protected del_command_alias_cmd
    arg cmdstr, com, template;
    
    if (sender() != this())
        throw(~perm, "Sender is not this");
    template = (template.explode("\""))[1];
    catch ~aliasnf {
        .del_command_alias(template);
    } with handler {
        $parse_lib.tell_error("No command alias is found matching the template \"" + template + "\".");
    }
    .tell("Command alias \"" + template + "\" deleted.");
.

protected command_aliases_cmd
    arg cmdstr, com, what;
    var aliases, a, line;
    
    if (!what)
        what = this();
    else
        what = .match_env_nice(what);
    if (what != this() && !what.is_writable_by(this()))
        $parse_lib.tell_error("You are not allowed to read the command aliases on " + what.namef());
    .tell("--- Command aliases on " + what.namef('xref) + ":");
    aliases = what.command_aliases();
    if (aliases) {
        for a in (aliases) {
            line = "  " + ("\"" + a[1] + "\"").pad((.linelen() - 10) / 2);
            .tell(line + " => \"" + a[2] + "\"");
        }
    } else {
        .tell("  <none>");
    }
    .tell("---");
.

public _tell
    arg what, [args];
    var line, conn, filter;
    
    if (sender() != this())
        throw(~perm, "Sender is not this.");
    if (type(what) == 'frob)
        what = $ctext_filter.filter(what, #[['formatter, $cml2_telnet_format]]);
    if (type(what) == 'list) {
        for line in (what)
            ._tell(line);
    } else {
        for conn in (connections)
            (| conn.write(what) |);
    }
.

public add_filter
    arg filter, [position];
    
    if (!sender().has_ancestor($filters) && !.is_writable_by(sender()))
        throw(~perms, "%O does not have permission to remove filters.", sender());
    
    // do this in three steps, first make sure the posistion is valid,
    // then check for it already, then figure its insert position.
    position = [@position, 'first][1];
    if (!(position in ['last, 'first]))
        throw(~type, "Posistion types must be one of: 'last, 'first.");
    
    // does it exist?  If so remove it.
    if (filter in filters)
        filters = filters.setremove(filter);
    
    // figure position
    if (position == 'last)
        position = filters.length() + 1;
    else if (position == 'first)
        position = 1;
    filters = filters.insert(position, filter);
.

public del_filter
    arg filter;
    
    if (!sender().has_ancestor($filters) && !.is_writable_by(sender()))
        throw(~perms, "%O does not have permission to remove filters.", sender());
    if (filters) {
        filters = filters.setremove(filter);
        return;
    } else {
        throw(~nofilters, "You do not have any tell filters.");
    }
.

protected wrap_cmd
    arg cmdstr, com, how;
    var filters;
    
    if (!(how in ["on", "off"]))
        return .tell("!  You can either turn line wrapping `on' or `off'");
    filters = .filters();
    if (how == "on") {
        if (filters && $wrap_filter in filters)
            return .tell("!  You already have line wrapping on..");
        .add_filter($wrap_filter);
        return .tell("Line wrapping turned on.");
    } else {
        if (filters && !($wrap_filter in filters))
            return .tell("! You dont have line wrapping turned on.");
        .del_filter($wrap_filter);
        return .tell("Line wrapping turned off.");
    }
.

public filters
    .perms(sender());
    return filters;
.

public tell_traceback
    arg traceback, [args];
    var tt, name, eargs, error, str;
    
    // tt = tell_traceback || ['verbose, 0, "! "];
    str = [@args, ""][1];
    eargs = [@args, 0, 0][2];
    error = [@args, 0, 0, 0][3];
    tt = ['verbose, -1, "! "];
    switch (tt[1]) {
        case 'verbose:
            traceback = $parse_lib.traceback(traceback, tt[2], tt[3], error);
            if (args)
                name = (| $list.to_english($list.map(args, 'namef, 'ref)) |);
            if (!name)
                name = "Object";
            .tell(traceback[1].replace("%O", name));
            .tell(traceback.subrange(2));
        default:
            .tell("! Internal error processing \"" + str + "\", contact an administrator.");
    }
.

public set_tell_traceback
    arg which, [lines];
    
    .perms(sender(), 'manager);
    if (!(which in ['verbose, 'brief, 'none]))
        throw(~type, "Which style must either be 'verbose, 'brief, or 'none.");
    if (lines && type(lines[1]) != 'integer)
        throw(~type, "You must specify the max lines as an integer.");
    if (!lines)
        lines = 0;
    else
        lines = lines[1];
    tell_traceback = [which, lines];
.

public namef
    arg [args];
    
    if (!args)
        args = [['name]];
    
    // first check for shortcuts, if so re-call this method correctly.
    if (args[1] == 'nactivity) {
        if (.activity())
            args = [['name], " (", ['activity], ")"] + args.subrange(2);
        else
            args = [['name]] + args.subrange(2);
    }
    if (args[1] == 'titled) {
        if (.title())
            args = [['name], ", ", ['title]] + args.subrange(2);
        else
            args = [['name]] + args.subrange(2);
    }
    return pass(@args);
.

protected password_cmd
    arg cmdstr, com, [args];
    var syn;
    
    syn = com + " <old password> <new password>";
    args = args[1].explode();
    if (args.length() < 2 || args.length() > 2)
        .tell_error(syn, "Specify old and new password (passwords cannot contain spaces).");
    if (!.check_password(args[1]))
        .tell_error(syn, "Sorry.");
    catch any {
        .set_password(args[2]);
    } with handler {
        .tell_error(syn, traceback()[1][2]);
    }
    .tell("Password changed.");
.

public set_title
    arg str;
    
    .perms(sender(), 'manager);
    if (str.length() > 30)
        throw(~type, "Titles must be under 30 characters.");
    title = str;
.

public match_context
    arg str;
    
    return context[str];
.

public context
    return context;
.

public match_environment
    arg str;
    var match, gend;
    
    if (!str && match = (| context['last] |)) {
        if (!valid(match))
            context = context.del('last);
        else
            return match;
    }
    if (match = (| (.remembered())[str] |)) {
        if (!valid(match))
            .del_remembered(str);
        else
            return match;
    }
    match = (> pass(str) <);
    if (match.has_ancestor($thing)) {
        gend = (| match.gender() |);
        if (gend)
            context = context.add(gend.pronoun('po), match);
    }
    context = context.add('last, match);
    return match;
.

public non_terminated_tell
    arg text;
    var conn;
    
    if ((| .setting("terminated-tell") |)) {
        for conn in (.connections())
            conn.write(text, 'non_terminated);
    } else {
        .tell(text);
    }
.

public set_name
    arg new_name, [ignore];
    var old_name, part, sname;
    
    (> .perms(sender(), 'manager) <);
    
    // so it doesnt bomb on .set_objname
    if ((> $user_db.valid_name(new_name) <))
        old_name = .name();
    catch any {
        (> pass(new_name, 'prop) <);
        if (!(| $user_db.key_changed(old_name, new_name) |))
            $user_db.insert(new_name, this());
        if (old_name != new_name) {
            sname = $string.strip(new_name, $user_db.stripped_characters());
            (> .set_objname(tosym("user_" + sname)) <);
        }
    } with handler {
        (| pass(old_name, 'prop) |);
        rethrow(error());
    }
.

public find_object_nice
    arg str, [args];
    var match;
    
    catch any {
        match = .find_object(str, @args);
    } with handler {
        .tell("!  " + traceback()[1][2]);
        throw(~stop, "", 'no_traceback);
    }
    return match;
.

public find_object
    arg str, [args];
    var trace, match;
    
    // comprehensive matching method.
    // args define what to match.
    if (!args)
        args = ['environment];
    while (args) {
        switch (args[1]) {
            case 'environment:
                match = (| .match_environment(str) |);
            case 'user:
                match = (| $user_db.find(str) |);
            case 'grasp:
                match = (| $object_lib.to_dbref(str) |);
        }
        if (match)
            return match;
        args = args.delete(1);
    }
    throw(~objnf, "No object found by the reference \"" + str + "\".");
.

protected age_cmd
    arg cmdstr, com, obj;
    var time, gender;
    
    .debug(obj);
    time = obj.created_on();
    if (obj.is($gendered))
        gender = obj.gender().pronoun('psc);
    else
        gender = "It";
    .tell(obj.namef() + " was created on " + $time.ldate(time));
    .tell(gender + " is " + $time.elapsed(time() - time, 'long) + " old.");
    if (obj.is($user))
        .tell(gender + " has logged " + $time.elapsed(obj.connected_seconds(), 'long) + " online.");
.

protected spawn_cmd
    arg cmdstr, com, args;
    var match, syn, new, name, parent, line, set;
    
    syn = "If object cannot have a name, <new name> will be the objname.";
    syn = [com + " <parent> <new name>", syn];
    args = args.explode_quoted();
    parent = .match_env_nice(args[1]);
    if (args.length() > 1 && args[2] == "named")
        args = args.delete(2);
    catch any {
        if (args.length() > 1)
            name = (> $code_lib.parse_name(args.subrange(2).to_string()) <);
        if (!parent.is($thing) && !.is($programmer))
            return "Only programmers are allowed to spawn non VR objects.";
        if (!parent.has_flag('fertile))
            return "Spawn failed: " + parent.namef('xref) + " is not fertile!";
        new = (> parent.spawn() <);
        .tell("Spawned new object " + new.namef('xref) + " from " + parent.namef('xref) + ".");
        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.objname();
        } else if (name) {
            if (!new.is($named))
                return new.objname() + " is not descended from $named, name " + toliteral(name[1][1]) + " not added.";
            (> new.set_name(@name[1]) <);
            if (name[2])
                (> new.set_name_aliases(@name[2]) <);
            return "Name set as: \"" + new.name() + "\"" + (new.name_aliases() ? " (" + new.name_aliases().to_english() + ")" | "");
        }
    } with handler {
        (| .tell_error(@syn, traceback()[1][2]) |);
        catch any {
            line = new.objname();
            if (!valid(new.destroy()))
                .tell("!  Sucessfully destroyed new object " + line + ".");
        }
    }
.

protected description_cmd
    arg cmdstr, cmd, str;
    var args, obj, syn, desc, long;
    
    syn = cmd + " [<what> as <description>|<what>]";
    args = $string.match_template("* as *", str);
    if (!args) {
        args = str.explode();
        if (!args)
            .tell_error(syn);
        obj = args.to_string();
        desc = .read();
    } else {
        obj = args[1];
        desc = args[3];
    }
    if (!desc) {
        .tell("No description given, aborting...");
        return;
    } else if (desc == 'aborted) {
        return;
    }
    obj = .match_env_nice(obj);
    catch any {
        obj.set_prose(desc);
        .tell("Description for " + obj.namef('ref) + " set.");
    } with handler {
        .tell(traceback()[1][2]);
    }
.

protected status_cmd
    arg cmdstr, com, [args];
    var line, s, x;
    
    s = $sys.status();
    for x in [1 .. s.length()] {
        if (s[x] < 0)
            s = s.replace(x, "(unknown)");
    }
    .tell("--- " + $motd.server_name() + " status report ---");
    if ($sys.dirty())
        .tell("System is:        dirty, will do normal backup.");
    else
        .tell("System is:        clean, will skip backup.");
    .tell("System lag:       " + $lag_watcher.lag());
    .tell("Next dbref:       " + toliteral($sys.next_dbref()));
    .tell("Driver:           " + $sys.server_info('driver_version, 'long));
    .tell("Core:             " + $sys.server_info('core_version, 'long));
    .tell("Started:          " + $time.to_english(time() - $sys.server_info('startup_time)) + " ago");
    .tell("Backup:     last: " + $time.to_english(time() - s[20]) + " ago");
    .tell("            next: " + $time.to_english(s[21] - time()) + " from now");
    .tell("        interval: " + $time.to_english(s[19]));
    .tell("Page:             " + tostr(s[9]) + " reclaims");
    .tell("                  " + tostr(s[10]) + " faults");
    .tell("Signals:          " + tostr(s[16]));
    .tell("Context switches: " + tostr(s[17]) + " voluntary");
    .tell("                  " + tostr(s[18]) + " involuntary");
.

public del_command_alias
    arg alias;
    
    (> .perms(sender()) <);
    (> pass(alias) <);
    if (!.command_aliases() && $command_aliases_parser in .parsers()) {
        .tell("Removing $command_aliases_parser from your list of parsers.");
        .del_parser($command_aliases_parser);
    }
.

public add_command_alias
    arg alias, actual;
    
    (> .perms(sender()) <);
    (> pass(alias, actual) <);
    if (.command_aliases() && !($command_aliases_parser in .parsers())) {
        .tell("Adding $command_aliases_parser to your list of parsers..");
        .add_parser($command_aliases_parser, 'first);
    }
.

public logout_connection
    arg connection;
    
    if (sender() != this() || definer() != caller())
        throw(~perm, "Invalid access to private method.");
    .tell($string.center("* * * " + $integer.n_to_nth(.connections().length() + 1) + " Logout sucessful * * *", .linelen()));
.

protected rehash_cmd
    arg cmdstr, cmd;
    
    .tell("Rehashing your command caches...");
    pause();
    .rehash_caches();
.

public description
    arg flags;
    var c, contents, ctext, pronoun, lines, p, br;
    
        pronoun = .gender().pronoun('psc);
        lines = [pronoun + " is " + (.activity() || "awake") + "."];
        if (contents = .contents()) {
            lines = lines + [pronoun + " is holding:"];
            for c in (contents)
                lines = lines + ["  " + c.namef()];
        } else {
            lines = [lines, pronoun + " is holding nothing."];
        }
        return (> pass(flags) <) + lines;
.

protected _who_at_place
    arg str;
    var place, thing, who, args;
    
    // This should actually be done with a global place dictionary and
    // a local place dictionary for each user.
    place = $place.match_descendants(str);
    if (!place) {
        .tell("I do not know where \"" + str + "\" is.");
        return 0;
    }
    who = [];
    for thing in (place.contents()) {
        if (thing.has_ancestor($user))
            who = [@who, thing];
    }
    if (!who) {
        .tell("Nobody is in " + place.namef() + ".");
        return 0;
    }
    args = [who, "Users in " + place.namef()];
    args = [@args, [['namef, 'titled], ['time_poll]]];
    args = [@args, ["Name", "Times (idle)"], [1, 1]];
    return args;
.

protected _who_is
    arg [args];
    var person, p, who;
    
    who = [];
    args = $string.explode_english_list(@args) || [];
    for p in (args) {
        catch any {
            person = $user_db.find(p);
        } with handler {
            switch (error()) {
                case ~ambig:
                    .tell("The name \"" + p + "\" can match any of: " + $list.to_english($list.map(traceback()[1][3], 'namef)) + ".");
                default:
                    .tell("I don't know who \"" + p + "\" is.");
            }
            continue;
        }
        who = [@who, person];
    }
    if (!who)
        return 0;
    return [who, who.length() == 1 ? "User" | "Users"];
.

protected _who_programmers
    arg args, all;
    var out, progs, p, x, t;
    
    progs = [];
    if (args && !all) {
        for p in (args) {
            x = $user_db.find(p);
            if (!x)
                .tell("I don't know who \"" + p + "\" is.");
            else
                progs = [@progs, x];
        }
        t = "Programmer" + progs.length() == 1 ? "" | "s";
    } else if (all) {
        t = "All Programmers";
        progs = $programmer.descendants();
        progs = progs.setremove($admin);
    } else {
        t = "Connected Programmers";
        for p in ($user_db.connected()) {
            if (p.has_ancestor($programmer))
                progs = [@progs, p];
        }
    }
    if (!progs)
        return 0;
    return [progs, t];
.

protected _who_admins
    arg args, all;
    var out, admins, a, x, t;
    
    admins = [];
    if (args && !all) {
        for a in (args) {
            x = $user_db.find(a);
            if (!x)
                .tell("I don't know who \"" + a + "\" is.");
            else
                admins = [@admins, x];
        }
        t = "Admin" + admins.length() == 1 ? "" | "s";
    } else if (all) {
        t = "All Admins";
        admins = $sys.admins();
    } else {
        t = "Connected Admins";
        for a in ($user_db.connected()) {
            if (a.has_ancestor($admin))
                admins = [@admins, a];
        }
    }
    if (!admins)
        return 0;
    return [admins, t];
.

protected _who_short
    var user, tmp, who, namestr, total;
    
    who = [];
    total = $user_db.connected().length();
    .tell("Currently connected users (total: " + tostr(total) + (total == 1 ? " person" | " people") + "):");
    for user in ($user_db.connected()) {
        namestr = " " + user.namef() + " (" + $time.elapsed(user.connected_at()) + " " + $time.dhms(user.idle_seconds()) + ")";
        who = [@who, namestr];
        if (tmp < namestr.length() + 2)
            tmp = namestr.length() + 2;
    }
    .tell($list.columnize(who, .linelen() / (tmp + 1), " ", .linelen()));
.

protected remember_cmd
    arg cmdstr, cmd, what, as, str;
    
    .add_remembered(what, str);
    return "Remembering " + what.namef('xref) + " as \"" + str + "\".";
.

protected forget_place
    arg place;
    
    if (type(place) != 'dbref)
        throw(~type, "Place must be submitted as an object.");
    remembered_places = remembered_places.setremove(place);
.

protected remembered_cmd
    arg cmdstr, cmd, args;
    var item, out;
    
    if (!.remembered())
        return .tell("You do not remember anything.");
    out = [];
    for item in (.remembered())
        out = out + ["  " + item[1] + " is " + item[2].namef('xref)];
    return ["Remembered Items:"] + out.lcolumnize();
.

protected forget_cmd
    arg cmdstr, cmd, str;
    var what;
    
    what = (| remembered[str] |);
    if (!what)
        return "You don't remember what \"" + str + "\" is...";
    .del_remembered(str);
    return "Forgetting " + what.namef('xref) + " as \"" + str + "\".";
.

public idle_time
    arg [args];
    var idle;
    
    args = [@args, 'dhms][1];
    idle = .idle_seconds();
    if (connected_at < 0 || idle < 30)
        return "";
    switch (args) {
        case 'dhms:
            return $time.dhms(idle, 'long);
        case 'elapsed:
            return $time.elapsed(idle);
        case 'seconds:
            return idle;
    }
.

public tell_error
    arg syntax, [problem];
    var problem, line, sprefix, prefix, length;
    
    // arg 1 == syntax
    // arg 2 == problem lines
    length = .linelen();
    if (syntax)
        .tell("=> Syntax: `" + syntax + "`");
    if (problem) {
        for line in (problem) {
            if (type(line) == 'string)
                line = line.wrap_lines(length, "!  ", 1);
            .tell(line);
        }
    }
    throw(~stop, "=> Syntax: `" + syntax + "`", 'no_traceback);
.

protected whereis_cmd
    arg cmdstr, cmd, who;
    var user, u;
    
    who = who && who.explode_english_list() || [];
    if (who && who[1] == "is")
        who = who.delete(1);
    if (!who)
        $parse_lib.tell_error("You must specify somebody.");
    for user in [1 .. who.length()] {
        u = (| $user_db.find(who[user]) |);
        if (!u) {
            .tell("I don't know who \"" + who[user] + "\" is.");
            who = who.replace(user, 0);
        } else {
            who = who.replace(user, u);
        }
    }
    for user in (who) {
        if (user)
            .tell(user.namef('nactivity) + " is in " + user.location().name('def));
    }
.

public connected_time
    arg [args];
    
    args = [@args, 'dhms][1];
    if (connected_at < 0)
        return "Last on: " + $time.ctime(abs(connected_at));
    switch (args) {
        case 'dhms:
            return $time.dhms(time() - connected_at, 'long);
        case 'elapsed:
            return $time.elapsed(time() - connected_at);
        case 'seconds:
            return time() - connected_at;
    }
.

public terminated_tell
    return (| settings[$user]["terminated-tell"] |) || "text/plain";
.

public connection_going_away
    arg addr, port;
    var con, line;
    
    catch any {
        (> .perms(caller(), $connection) <);
        for con in (connections) {
            if (!valid(con))
                connections = connections.setremove(con);
        }
        con = sender() in connections;
        connections = connections.setremove(sender());
        if (!connections)
            .logout(sender());
        else
            .logout_connection(sender());
        line = "DISCONNECT " + tostr(con) + " (";
        line = line + (.parents())[1].objname() + "): ";
        line = line + .objname() + " <" + addr + ">";
        (| $sys.log(line) |);
    } with handler {
        $sys.log($parse_lib.traceback(traceback()));
    }
.

public read
    arg [args];
    var text, output, head, tail;
    
    if (!.connections())
        return 'not_connected;
    if ((.connections())[1].is_reading_block())
        return 'engaged;
    head = [@args, "Now receiving input, enter \".\" to finish or \"@abort\" to abort."][1];
    tail = [@args, "** Aborted **; Text thrown away.", "** Aborted **; Text thrown away."][2];
    if (head)
        .tell(head);
    output = (.connections())[1].start_reading_block('multiple);
    if (output == 'aborted && tail)
        .tell(tail);
    return output;
.

public read_line
    arg [args];
    var line, abort_msg, head;
    
    if (!.connections())
        return 'not_connected;
    if ((.connections())[1].is_reading_block())
        return 'engaged;
    head = [@args, ""][1];
    abort_msg = [@args, "** Aborted **", "** Aborted **"][2];
    if (head)
        .tell(head);
    line = (.connections())[1].start_reading_block('one);
    if (line == 'aborted && abort_msg)
        .tell(abort_msg);
    return line[1];
.

public prompt
    arg prompt, [abort_msg];
    
    .non_terminated_tell(prompt);
    return (> .read_line("", @abort_msg) <);
.

protected ways_cmd
    arg cmdstr, cmd;
    var exits, e, line, out;
    
    exits = .location().exits_visible_to(this());
    if (!exits)
        return "There are no visible exits from this room.";
    
    // come up with some settings to configure how to display exits
    // for now they can deal with the following
    out = ["Visible exits from " + .location().name() + ":"];
    for e in (exits)
        out = out + ["  " + e.name() + (e.name_aliases() ? " (" + e.name_aliases().to_english() + ")" | "") + " to " + e.dest().name()];
    return out + ["---"];
.

public content_type
    return (| settings_data[$user]["content-type"] |) || "text/plain";
.

public set_objname: disallow_overrides
    arg new_objname;
    
    if (caller() != $user && !(sender() in $sys.system()))
        throw(~perm, "User objnames can only be changed by $user.");
    (> pass(new_objname) <);
.

protected context_cmd
    arg [args];
    var out;
    
    out = "Last thing: " + ((| context['last].name() |) || "(nothing)");
    out = [out, "Last it:    " + ((| context["it"].name() |) || "(nothing)")];
    out = [out, "Last her:   " + ((| context["her"].name() |) || "(nothing)")];
    out = [out, "Last him:   " + ((| context["him"].name() |) || "(nothing)")];
    return out;
.

public del_channel
    arg channel;
    
    if (sender() != this())
        (> .perms(sender(), 'system) <);
    if (!channels)
        return;
    channels = channels.del(channel);
    if (!channels)
        channels = 0;
.

public add_channel
    arg channel, [value];
    
    if (sender() != this())
        (> .perms(sender(), 'system) <);
    if (!channels)
        channels = #[];
    if (!value)
        value = 1;
    channels = channels.add(channel, value);
.

public listens_to_channel
    arg channel, [args];
    
    if (sender() != this())
        (> .perms(sender(), 'system) <);
    if (!channels)
        return 1;
    if (args && channel == 'login && (| type(channels['login]) == 'list |))
        return args[1] in channels['login];
    return channel in channels.keys();
.

protected look_cmd
    arg [args];
    
    .ptell(.location().get_description(), #[['type, 'description], ['dtype, 'location], ['object, .location()]]);
.

public walk_path
    arg [path];
    var p, exit;
    
    for p in (path) {
        // we need to go through the command parser, because it can
        // see all of the exits.
        exit = $command_parser.parse(this(), p, $null_parser);
        if (exit == 'failed)
            throw(~invpath, "No exit " + toliteral(p) + " from " + .location().name());
        exit[1].invoke(#[['nofork, 1], ['prose, 0], ['extra, 0]]);
        pause();
    }
    .look_cmd();
.

public set_content_type
    arg definer, name, value, [args];
    var sets;
    
    (> .set_local_setting(definer, name, value, @args) <);
    if (value == "text/html")
        evaluator = $html_formatter;
    else
        evaluator = $format_evaluator;
.

public check_content_type
    arg definer, value, [args];
    var sets;
    
    if (!(value in ["text/plain", "text/html"]))
        throw(~check, "Content-type must be either \"text/plain\" or \"text/html\".");
    return value;
.

public evaluator
    return evaluator;
.

public check_experienced
    arg definer, value, [args];
    
    // called by the settings system
    if (value in ["", "y", "ye", "yes"])
        return "yes";
    else
        return "";
.

protected get_cmd
    arg cmdstr, cmd, what;
    var ol, l;
    
    if (what.location() == sender()) {
        return "You already have " + what.name('def) + ".";
    } else if (what == this()) {
        return "Ewww, its all twisty.";
    } else {
        catch any {
            ol = what.location();
            l = .location();
            (> what.move_to(this()) <);
            if (l != ol)
                (| l.announce(.namef() + " takes " + what.namef() + ".", this(), what) |);
            (| l.announce(.namef() + " takes " + what.namef() + ".", this(), what) |);
            return "You take " + what.namef() + ".";
        } with handler {
            if (error() == ~locked)
                return "You are unable to " + cmd + " " + what.name() + "!";
            else
                return traceback()[1][2];
        }
    }
.

protected drop_cmd
    arg cmdstr, cmd, what;
    
    if (!(what in .contents())) {
        return "You don't have " + what.name('def) + ".";
    } else {
        catch any {
            (> what.move_to(.location()) <);
            .tell("You drop " + what.namef() + ".");
            (| .location().announce(.namef() + " drops " + what.namef() + ".", this(), what) |);
        } with handler {
            return traceback()[1][2];
        }
    }
.

public announce
    arg [args];
    
    return .location().announce(@args);
.

protected take_from_cmd
    arg cmdstr, cmd, what, p, where;
    var c, obj, l, wl;
    
    if (where == this())
        return "You already have " + what.name() + ".";
    for c in (where.contents()) {
        if (c.match_name(what))
            obj = c;
    }
    if (!obj)
        return .name() + " does not have " + what;
    if (obj == this() || obj == where)
        return "Ewww, its all twisty.";
    catch any {
        l = .location();
        wl = where.location();
        (> obj.move_to(this()) <);
        if (l != wl)
            wl.announce(.name() + " " + cmd + "s " + obj.name() + " " + p + " " + where.name(), this());
        l.announce(.name() + " " + cmd + "s " + obj.name() + " " + p + " " + where.name(), this());
        return "You " + cmd + " " + obj.name() + " " + where + " " + where.name() + ".";
    } with handler {
        switch (error()) {
            case ~locked:
                if (where.is($user))
                    where.tell(.namef() + " tried to take " + obj.name() + " from you.");
                return obj.name() + " is locked to " + obj.lock().lock_name('thing) + ".";
            default:
                return traceback()[1][2];
        }
    }
.

public set_activity
    arg str;
    
    (> .perms(sender()) <);
    activity = str;
.

public is_tellable_by
    arg caller, sender;
    
    if (!(caller in [$user, $programmer, $admin, $mail_ui, $help_ui, $messages_ui, $settings_ui]) && sender != this())
        return 0;
.

protected go_cmd
    arg cmdstr, cmd, path;
    
    path = path.explode_quoted();
    .walk_path(@path);
.

public name
    arg [args];
    
    if (activity)
        return (> pass(@args) <) + " (" + activity + ")";
    else
        return (> pass(@args) <);
.

public ptell
    arg what, flags;
    
    if (!.is_tellable_by(caller(), sender()))
        throw(~perm, "Only allowed objects may call protected tell.");
    switch ((| flags['type] |) || 'interaction) {
        case 'channel:
            if (!.listens_to_channel(@args.subrange(2)))
                return;
    }
    ._tell(what);
.

public connection_starting
    arg addr, port;
    var line, c;
    
    (> .perms(caller(), $connection) <);
        for c in (connections) {
            if (!valid(c))
                connections = connections.setremove(c);
        }
        connections = connections.setadd(sender());
        if (connections.length() == 1)
            (| .login(sender()) |);
        else
            (| .login_again(sender()) |);
        line = "CONNECT " + tostr(sender() in connections) + " (";
        line = line + (.parents())[1].objname() + "): ";
        line = line + .objname() + " <" + sender().address() + "> ";
        (| $sys.log(line) |);
.

public purge_caches
    if (!.connected())
        return (> pass() <);
.

protected reset_parsers
    parsers = [$command_parser];
.

public connected_seconds
    return connected_seconds;
.

protected add_trustee_cmd
    arg cmdstr, cmd, args;
    var syn, obj, trustee;
    
    args = args.replace(" to ", " ").explode();
    if (!args || args.length() != 2)
        .tell_error(cmd + " <trustee> [to] <object>");
    trustee = .match_env_nice(args[1]);
    obj = .match_env_nice(args[2]);
    catch any {
        obj.add_trusted(trustee);
        return ["Sucessfully added trustee " + trustee.namef('xref) + " to " + obj.namef('xref), "New trusted list: " + obj.trusted('literal).compress().map('namef, 'xref).to_english() + "."];
    } with handler {
        .tell(traceback()[1][2]);
    }
.

protected del_trustee_cmd
    arg cmdstr, cmd, args;
    var syn, obj, trustee;
    
    args = args.replace(" from ", " ").explode();
    if (!args || args.length() != 2)
        .tell_error(cmd + " <trustee> [from] <object>");
    trustee = .match_env_nice(args[1]);
    obj = .match_env_nice(args[2]);
    catch any {
        obj.del_trusted(trustee);
        return ["Sucessfully removed trustee " + trustee.namef('xref) + " from " + obj.namef('xref), "New trusted list: " + obj.trusted('literal).compress().map('namef, 'xref).to_english() + "."];
    } with handler {
        .tell(traceback()[1][2]);
    }
.

protected add_writer_cmd
    arg cmdstr, cmd, args;
    var syn, obj, writer;
    
    args = args.replace(" to ", " ").explode();
    if (!args || args.length() != 2)
        .tell_error(cmd + " <writer> [to] <object>");
    writer = .match_env_nice(args[1]);
    obj = .match_env_nice(args[2]);
    catch any {
        obj.add_writer(writer);
        return ["Sucessfully added writer " + writer.namef('xref) + " to " + obj.namef('xref), "New writers list: " + obj.writers('literal).compress().map('namef, 'xref).to_english() + "."];
    } with handler {
        .tell(traceback()[1][2]);
    }
.

protected del_writer_cmd
    arg cmdstr, cmd, args;
    var syn, obj, writer;
    
    args = args.replace(" from ", " ").explode();
    if (!args || args.length() != 2)
        .tell_error(cmd + " <writer> [from] <object>");
    writer = .match_env_nice(args[1]);
    obj = .match_env_nice(args[2]);
    catch any {
        obj.del_writer(writer);
        return ["Sucessfully removed writer " + writer.namef('xref) + " from " + obj.namef('xref), "New writers list: " + obj.writers('literal).compress().map('namef, 'xref).to_english() + "."];
    } with handler {
        .tell(traceback()[1][2]);
    }
.

protected writes_cmd
    arg cmdstr, cmd, args;
    var target, writes, obj, out, len;
    
    target = (| .match_environment(args) |);
    if (!target) {
        target = (| $user_db.find(args) |);
        if (!target)
            return "Unable to find \"" + args + "\".";
    }
    writes = target.writes();
    if (!writes)
        return target.namef('ref) + " is not in any object's writers list.";
    out = [target.namef('ref) + " is in the writers list for:"];
    return out + $code_lib.generate_object_listing(writes, 'writers, 'literal);
.

protected managed_cmd
    arg cmdstr, cmd, args;
    var manager, managed, obj, out, len;
    
    manager = (| .match_environment(args) |);
    if (!manager) {
        manager = (| $user_db.find(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)
        out = out + ["  " + obj.namef('xref).pad(len) + " " + $object_lib.see_perms(obj, ["", ""])];
    return out;
.

protected owned_cmd
    arg cmdstr, cmd, args;
    var target, owned, obj, line, name, out, len, mlen, llen;
    
    target = (| .match_environment(args) |);
    if (!target) {
        target = (| $user_db.find(args) |);
        if (!target)
            return "Unable to find \"" + args + "\".";
    }
    owned = target.owned();
    if (!owned)
        return target.namef('ref) + " does not own any objects list.";
    out = [target.namef('ref) + " owns:"];
    return out + $code_lib.generate_object_listing(owned, 'owners);
.

protected add_remembered
    arg what, name;
    
    remembered = (remembered || #[]).add(name, what);
.

protected del_remembered
    arg name;
    
    remembered = (remembered || #[]).del(name);
    if (!remembered)
        clear_var('remembered);
.

public remembered
    return remembered || #[];
.



parent $location
parent $public
object $place

var $root child_index 147
var $root fertile 1
var $root inited 1
var $root objname 'place
var $root created_on 796268969
var $root flags ['parameters, 'methods, 'code, 'fertile, 'core]
var $gendered gender $gender_neuter
var $described prose []
var $messaged verbs #[]
var $named name ['uniq, "place", "the place"]
var $named name_aliases []
var $place realm 0
var $place exits []
var $place coordinates 0
var $place darkness 0
var $public public ['readable]
var $location contents []
var $command_cache shortcut_cache []
var $command_cache remote_cache 0
var $command_cache local_cache 0
var $has_settings defined_settings #[["public-home", #[['get, 'get_local_setting], ['set, 'set_local_setting], ['check, 'is_boolean], ['del, 'delete_local_setting], ['check_args, []], ['get_args, []], ['set_args, []], ['display, 'display_boolean_yes_no]]], ["realm", #[['get, 'realm], ['set, 'set_realm], ['check, 'check_realm], ['del, 'del_realm], ['check_args, []], ['get_args, []], ['set_args, []], ['display, 'display_setting_realm]]], ["darkness", #[['get, 'darkness], ['set, 'set_darkness], ['check, 'is_type], ['del, 'delete_local_setting], ['check_args, ['integer]], ['get_args, []], ['set_args, []]]]]

root init_place
    exits = [];
    realm = $realm_of_creation.new();
    coordinates = #[];
.

root uninit_place
    var x;
    
    for x in (exits)
        (| x.place_destroyed() |);
    (| realm.place_destroyed(this()) |);
    for x in (.area())
        (| x.place_destroyed(this()) |);
    (| $place_db.place_destroyed() |);
.

public environment
    return pass() + exits;
.

public description
    arg flags;
    
    return (> pass(flags) <) + ._desc_contents(flags);
.

public exits
    (| .perms(caller(), definer(), $command_parser) |) || (> .perms(sender()) <);
    
    // check a callers list or something, so that not any joe schmo can check
    // this list (limit to any core object...?)
    return exits;
.

public add_exit: disallow_overrides
    arg coord1, coord2;
    var exit;
    
    // coordinates are not standard x/y, but radial/azimuth
    .perms($exit, caller());
    exit = sender();
    (> ._add_exit(exit) <);
    (> ._add_coordinate(exit, coord1, coord2) <);
    (> ._add_coordinate(exit.dest(), coord1, coord2) <);
.

public _add_exit: disallow_overrides
    arg obj;
    
    // use .add_exit()
    .perms(sender(), 'this);
    exits = [@exits, obj];
.

public _del_exit: disallow_overrides
    arg obj;
    
    // use .del_exit()
    .perms(sender(), 'this);
    exits = exits.setremove(obj);
.

public del_exit: disallow_overrides
    var exit;
    
    exit = sender();
    .perms(caller(), $exit);
    (| ._del_exit(exit) |);
    (| ._del_coordinate(exit) |);
    (| ._del_coordinate(exit.dest()) |);
.

public _add_coordinate
    arg obj, coord1, coord2;
    
    .perms(sender(), 'this);
    
    // should only be called from inside this object.
    coordinates = coordinates.add(obj, [coord1, coord2]);
.

public _del_coordinate
    arg obj;
    
    .perms(sender(), 'this);
    
    // should only be called from inside this object.
    coordinates = coordinates.del(obj);
.

public did_connect
    if (.visibility() >= 0)
        .announce(sender().namef('titled) + " wakes up.", sender());
.

public did_disconnect
    if (.visibility() >= 0)
        .announce(sender().namef('titled) + " falls asleep.", sender());
.

public realm
    arg [args];
    var tmp, r;
    
    return realm;
.

public announce
    arg str, [except];
    var obj, part;
    
    if (type(str) == 'frob && class(str) == $message_frob && this() in str.parts()) {
        part = str.get_part(this());
        str = str.del_entry(this());
        str = str.add_entry("general", part);
    }
    for obj in (.contents()) {
        if (!(obj in except))
            (| obj.tell(str) |);
    }
.

public did_housekeep
    arg who;
    var message;
    
    // catch ~msgnf {
    catch any {
        message = .get_message('housekeeper);
    } with handler {
        message = "The housekeeper hauls %N away";
    }
    
    // this will have to do until we get a correct action setup
    .announce(message.replace("%N", who.namef()));
.

public set_name
    arg new_name, [args];
    var old_name;
    
    old_name = .name();
    (> pass(new_name, @args) <);
    (| $place_db.room_changed_name(old_name) |);
.

public set_realm
    arg definer, name, value, [args];
    
    (> .perms(sender(), 'this) <);
    realm = value;
.

public short_description
    arg actor, [exclude];
    var output, dark;
    
    output = pass(actor, exclude);
    output = output + ._desc_contents(actor, exclude);
    output = output + ._desc_exits(actor, exclude);
    return output;
.

public _desc_contents
    arg flags;
    var users, br, exclude, actor, objects, output, obj, line;
    
    // called by .description
    (> .perms(sender(), 'this) <);
    users = [];
    objects = [];
    actor = flags['actor];
    exclude = flags['exclude];
    for obj in (.contents()) {
        if (!(obj in exclude) && (| obj.is_obvious_to(actor) |)) {
            if (obj.has_ancestor($body))
                users = [@users, obj.namef('nactivity)];
            else
                objects = [@objects, obj.name()];
        }
    }
    output = [];
    br = (<$ctext_format, ["br", #[], #[], [], 'do_br]>);
    if (users) {
        line = $list.to_english(users) + " ";
        line = line + (users.length() > 1 ? "are" | "is") + " here.";
    
        //output = [@output, br, line];
        output = [@output, line];
    }
    if (objects) {
        line = "You see ";
        line = line + $list.to_english(objects) + " here.";
    
        //output = [@output, br, line];
        output = [@output, line];
    }
    return output;
.

public exits_visible_to
    arg who;
    var obv, exit;
    
    obv = [];
    for exit in (.exits()) {
        if (exit.is_visible_to(who))
            obv = obv + [exit];
    }
    return obv;
.

public visible_exits
    var obv, exit;
    
    obv = [];
    for exit in (.exits()) {
        if (exit.visibility() >= .visibility())
            obv = [@obv, exit];
    }
    return obv;
.

public area
    var out, x;
    
    out = [];
    for x in (coordinates.keys()) {
        if (x.has_ancestor($place))
            out = out + [x];
    }
    return out;
.

public realm_name
    arg [ctype];
    
    ctype = [@ctype, "text/plain"][1];
    switch (ctype) {
        case "text/html":
            return .hname() + " (" + realm.realm_name() + ")";
        default:
            return .name() + " (" + realm.realm_name() + ")";
    }
.

public will_attach
    arg type, [by_whom];
    
    by_whom = [@by_whom, sender()][1];
    if (!(| .trusts(by_whom) |) && !.is_publicly('extendable))
        throw(~perm, "Place is not publicly extendable.");
.

public visible_exits_formatted
    arg actor, how;
    var output, ex, exits, line;
    
    exits = .visible_exits();
    switch (how) {
        case 'none:
            return [];
        case 'brief:
            if (!exits)
                return [];
            return ["Exits: " + $list.to_english($list.map(exits, 'name), "none")];
        case 'average:
            output = [];
            for ex in (exits) {
                line = ex.name() + " (";
                line = line + $list.to_english(ex.name_aliases(), "no, aliases");
                output = [@output, line];
            }
            return output ? ["Exits: " + $list.to_english(output)] | [];
        case 'long:
            output = [];
            for ex in (exits)
                output = [@output, "  " + ex.name() + " (to " + ex.dest().name() + ")"];
            return output ? ["Exits: ", @$list.lcolumnize(output, actor.linelen())] | [];
        case 'verbose:
            output = [];
            for ex in (exits) {
                line = ex.prose();
                if (!line)
                    line = [ex.name()];
                output = [@output, line[1]];
            }
            return output ? [output.to_string()] | [];
    }
.

public place_destroyed
    arg place;
    
    // announces when a place is destroyed
    (> .perms(caller(), $place, $realms_frob) <);
    (| ._del_coordinate(place) |);
.

public exit_arrival
    arg actor;
    var exit, omsg, msg;
    
    exit = sender();
    msg = (| exit.message('arrive) |);
    omsg = (| exit.message('oarrive) |) || "%N arrives.";
    
    // should be ctext...
    if (msg) {
        msg = msg.replace("%N", actor.name());
        msg = msg.replace("%E", exit.name());
        actor.tell(msg);
    }
    omsg = omsg.replace("%N", actor.name());
    omsg = omsg.replace("%E", exit.name());
    .announce(omsg, actor);
.

public exit_departure
    arg actor;
    var exit, msg, omsg;
    
    exit = sender();
    
    // remove the crit's later, it will have 4 whopping ticks
    msg = (| exit.message('depart) |);
    omsg = (| exit.message('odepart) |) || "%N leaves.";
    
    // should be ctext...
    if (msg) {
        msg = msg.replace("%N", actor.name());
        msg = msg.replace("%E", exit.name());
        actor.tell(msg);
    }
    omsg = omsg.replace("%N", actor.name());
    omsg = omsg.replace("%E", exit.name());
    .announce(omsg, actor);
.

public long_description
    arg actor, [exclude];
    var output, dark;
    
    output = pass(actor, exclude);
    output = output + ._desc_contents(actor, exclude);
    output = output + ._desc_exits(actor, exclude);
    return output;
.

public announce_to
    arg type, str, [except];
    var obj;
    
    switch (type) {
        case 'realm, 'sub_realm:
            .realm().announce('sub, str, @except);
        case 'super_realm:
            .realm().announce('super, str, @except);
        case 'local:
            .announce(str, @except);
        case 'area:
            for obj in (.area() + [this()])
                obj.announce(str, @except);
    }
.

public set_objname: disallow_overrides
    arg new_objname;
    
    if (caller() != $place && !(sender() in $sys.system()))
        throw(~perm, "Place objname can only be changed by $place.");
    (> pass(new_objname) <);
.

public mesg_announce
    arg mesg;
    var obj;
    
    //workaround to allow message to work.
    for obj in (.contents())
        (| obj.tell(mesg, 'ctext) |);
.

public did_attach
    arg type, [by_whom];
    
    if (caller() != $exit)
        throw(~perm, "Caller is not $exit.");
    .add_object_to_remote_caches(sender());
.

public rehash_caches
    var exit;
    
    (> pass() <);
    for exit in (exits)
        (> .add_object_to_remote_caches(exit) <);
.

public coordinates
    return coordinates;
.

public check_realm
    arg definer, value, [args];
    
    return $places.match_realm(value) || throw(~invrealm, "Unable to find the realm " + toliteral(value) + ".");
.

public del_realm
    arg name, definer, [args];
    
    (> .perms(sender()) <);
    realm = $realm_of_creation;
.

public darkness
    arg [args];
    
    return darkness;
.

public display_setting_realm
    arg frob;
    
    return frob.name();
.



parent $network
object $connection

var $root objname 'connection
var $root child_index 6
var $root created_on 809051864
var $root inited 1
var $root indestructable 1
var $root flags ['parameters, 'methods, 'code, 'core]
var $connection buffer `[]
var $connection host ""
var $connection daemon 0
var $connection active 0
var $connection line_buffer 0
var $connection interface 0
var $connection timeout 0
var $connection read_block []
var $connection started_at 0
var $connection port 0

public new
    var child, daemon, port, i;
    
    (> .perms(caller(), $daemon) <);
    child = .spawn();
    daemon = sender();
    i = interface.new(child);
    port = daemon.current_port();
    
    // do perms stuff
    child.add_writer(daemon);
    child.add_writer(interface);
    child.add_writer((| class(i) |) || i);
    child.add_writer(this());
    child.set_daemon(daemon);
    child.set_port(port);
    child.new_interface(i);
    child.change_manager(child);
    
    // call scheduler
    // if (timeout) {
    // }
    return child;
.

public daemon_shutdown
    var c;
    
    (> .perms(caller(), $daemon) <);
    for c in (.children())
        (> c.close() <);
.

root init_connection
    buffer = `[];
    host = "";
    daemon = 0;
    active = 0;
    line_buffer = [];
    interface = 0;
    timeout = 0;
    read_block = 0;
    started_at = 0;
    .set_flags([]);
.

root uninit_connection
    (| close_connection() |);
    active = 0;
    if (interface)
        (| interface.connection_going_away(.address(), daemon.current_port()) |);
    interface = 0;
.

public timeout
    return timeout;
.

public address
    (> .perms(sender()) <);
    return host;
.

public set_host
    arg host;
    
    (> .perms(sender()) <);
    set_var('host, host);
.

public change_interface
    arg new;
    var old;
    
    (> .perms(sender()) <);
    if (interface) {
        old = interface;
        old.connection_going_away(.address(), daemon.current_port());
        .del_writer((| class(old) |) || old);
    }
    interface = new;
    .add_writer((| class(new) |) || new);
    interface.connection_starting(.address(), daemon.current_port());
.

public new_interface
    arg interface;
    
    (> .perms(sender()) <);
    set_var('interface, interface);
.

public set_daemon
    arg daemon;
    
    (> .perms(sender()) <);
    set_var('daemon, daemon);
.

public write
    arg what, [how];
    var elem, sep;
    
    sep = 'non_terminated in how ? `[] | `[10];
    switch (type(what)) {
        case 'string:
            what = $buffer.from_strings([what], sep);
        case 'list:
            what = $buffer.from_strings(what, sep);
        case 'buffer:
        default:
            throw(~type, "Write: strings, list of strings and buffers.");
    }
    echo(what);
.

driver parse
    arg incoming;
    var lines, line, index;
    
    lines = buffer.append(incoming).to_strings();
    index = lines.length();
    buffer = lines[index];
    lines = lines.delete(index);
    line_buffer = [@line_buffer, @lines];
    while (line_buffer) {
        line = line_buffer[1];
        line_buffer = line_buffer.delete(1);
        (| .parse_line(line) |);
    }
.

public parse_line
    arg line;
    
    (> .perms(sender()) <);
    if (read_block) {
        read_block = read_block.parse(line);
        line = .rehash_read_status();
        if (!line && line != "")
            return;
    }
    if (interface.parse_line(line) == 'disconnect)
        (> .close() <);
.

public echo_file
    arg fname;
    
    (> .perms(sender()) <);
    (> echo_file(fname) <);
.

public is_reading_block
    return read_block ? 1 | 0;
.

public finish_reading_block
    var task_id, lines;
    
    (> .perms(sender()) <);
    task_id = read_block.task_id();
    lines = read_block.lines();
    read_block = 0;
    $sys.resume(task_id, lines);
.

public abort_reading_block
    (> .perms(sender()) <);
    read_block = read_block.add('lines, 'aborted);
    .finish_reading_block();
.

public start_reading_block
    arg count;
    
    (> .perms(sender()) <);
    read_block = $read_parser.new(task_id(), count);
    return $sys.suspend();
.

public rehash_read_status
    (> .perms(sender(), 'this) <);
    switch (read_block.status()) {
        case 'abort:
            .abort_reading_block();
        case 'not_done:
            // do nothing
        case 'done:
            .finish_reading_block();
        case 'pass_command:
            return read_block.command();
    }
    return 0;
.

public do_timeout
    arg seconds;
    
    (> .perms(sender(), $scheduler) <);
    .write("Timeout (" + tostr(seconds) + ")");
    .close();
.

public close
    (> .perms(sender()) <);
    (> .destroy() <);
.

public disconnect
    arg [args];
    
    if (sender())
        throw(~perm, "Sender is not the driver.");
    (| .close() |);
.

public set_port
    arg port;
    
    (> .perms(sender()) <);
    set_var('port, port);
.

public init_connection_settings
    (> .perms(caller(), $connection) <);
.

public active
    return active;
.

driver connect
    arg host, socket;
    
    set_var('host, host);
    daemon.new_connection();
.

public write_string
    arg string;
    
    if (sender() != interface && sender() != (| class(interface) |))
        throw(~perm, sender().objname() + " cannot write this connection.");
    if (type(string) != 'string)
        throw(~type, "Argument must be a string.");
    echo($buffer.from_string(string));
.

public write_strings
    arg strings;
    
    if (sender() != interface && sender() != (| class(interface) |))
        throw(~perm, sender().objname() + " cannot write this connection.");
    if (type(strings) != 'list)
        throw(~type, "Argument must be a list of strings.");
    echo($buffer.from_strings(string));
.

public write_buffer
    arg buffer;
    
    if (sender() != interface && sender() != (| class(interface) |))
        throw(~perm, sender().objname() + " cannot write this connection.");
    if (type(buffer) != 'buffer)
        throw(~type, "Argument must be a buffer.");
    echo(buffer);
.

public active_since
    return active;
.

public start
    // Make this method 'fork' from the regular thread stack
    (> .perms(caller(), $daemon) <);
        active = time();
        interface.connection_starting(host, port);
.

public interface
    caller() == $daemon || (> .perms(sender()) <);
    return interface;
.



parent $network
object $daemon

var $root objname 'daemon
var $root child_index 2
var $root created_on 809051864
var $root inited 1
var $root indestructable 1
var $root flags ['parameters, 'methods, 'code, 'core]
var $daemon default_port 0
var $daemon connection 0
var $daemon next_connection 0
var $daemon current_port 0

public set_default_port
    arg port;
    
    (> .perms(sender()) <);
    default_port = port;
.

public set_connection
    arg connection;
    
    (> .perms(sender()) <);
    set_var('connection, connection);
.

public stop_listening
    arg [port];
    
    (> .perms(sender()) <);
        connection.daemon_shutdown();
        next_connection = 0;
.

public start_listening
    arg [port];
    
    (> .perms(sender()) <);
    (| .stop_listening() |);
    current_port = [@port, default_port][1];
    next_connection = connection.new();
    bind_port(current_port, next_connection);
.

public new_connection
    var new_connection;
    
    (> .perms(caller(), $connection, definer()) <);
    if (!valid(next_connection))
        next_connection = connection.new();
    new_connection = next_connection;
    next_connection = connection.new();
    bind_port(current_port, next_connection);
    
    // With forked methods, put this before we create the new connection
    new_connection.start();
.

public startup
    arg [args];
    var name, opt, port;
    
    catch any {
        (> .perms(caller(), $sys) <);
        name = tostr(.objname('symbol));
        name = "-p" + name.subrange(1, ("_" in name) - 1);
        opt = name in args;
        port = (| toint(args[opt + 1]) |) || default_port;
        (| .stop_listening() |);
        (> .start_listening(port) <);
        $sys.log("** Starting " + .objname() + " on port " + tostr(port) + " **");
    } with handler {
        switch (error()) {
            case ~bind:
                $sys.log("** Unable to bind to port " + tostr(port) + "! **");
            default:
                $sys.log($parse_lib.traceback(traceback()));
        }
    }
.

public current_port
    return current_port;
.

public bind_port
    arg port;
    
    if (caller() == $backdoor)
        return (> bind_port(port, sender()) <);
.

public shutdown
    arg [args];
    
    (> .perms(caller(), $sys) <);
    (> .stop_listening() <);
.



parent $network
parent $command_cache
object $connection_interface

var $root objname 'connection_interface
var $root child_index 2
var $root created_on 809051864
var $root inited 1
var $root quota_exempt -1
var $root indestructable 1
var $root flags ['parameters, 'methods, 'code, 'core]
var $command_cache shortcut_cache 0
var $command_cache remote_cache 0
var $command_cache local_cache 0
var $has_commands shortcuts #[]
var $has_commands remote #[]
var $has_commands local #[]
var $connection_interface connection 0

public parse_line
    arg this, line;
    var cmd, c, match, parsed, i, m, a, u;
    
    catch any {
        while (line && line[1] == " ")
            line = line.subrange(2);
        if (!line) {
            return .null_cmd(this, line);
        } else {
            cmd = line.explode();
            cmd = [line, cmd[1], cmd.subrange(2).to_string() || ""];
            c = (| .match_in_local_cache(@cmd) |);
            if (c && c[1] == 'local) {
                // screw duplicates, take the first match
                match = c[2][1];
                m = match[2];
                i = match[5];
                parsed = i.keys();
                for a in [1 .. m.length()] {
                    if (a in parsed)
                        m = m.replace(a + 2, (> $command_lib.convert_arg(i[a][1], m[a + 2], $no_one, i[a][2] ? i[a][2][1] | $no_one, $no_one) <));
                }
                return (> .(match[4])(this, @m) <);
            }
            return (> .invalid_cmd(this, line) <);
        }
    } with handler {
        if (traceback()[1][3] != 'no_traceback) {
            this['connection].write($parse_lib.traceback(traceback()));
            return 'disconnect;
        }
    }
.

public connection_going_away
    arg [args];
    
    (| .destroy() |);
.

public linelen
    arg [args];
    
    return 79;
.

public send
    arg [args];
    
    return (> .write(@args) <);
.

public write
    arg this, what;
    
    return (> this['connection].write(what) <);
.

public null_cmd
    arg [args];
    
    return 'disconnect;
.

public invalid_cmd
    arg [args];
    
    return 'disconnect;
.

public connection
    return connection;
.

public tell
    arg [args];
    
    (> .write(@args) <);
.

public new
    arg c;
    var i;
    
    (> .perms(caller(), $connection) <);
    i = .spawn();
    i.set_connection(c);
    i.change_manager(i);
    return i;
.

public new_connection
    arg this, host, port;
    
.

public set_connection
    arg c;
    
    (> .perms(caller(), definer()) <);
    connection = c;
.

public daemon_shutdown
    var i;
    
    (> .perms(caller(), $daemon) <);
    for i in (.children())
        (| i.destroy() |);
.



parent $help_root
parent $named
object $help_index

var $root child_index 1
var $root inited 1
var $root trusted 1
var $root objname 'help_index
var $root created_on 796268969
var $root flags ['parameters, 'methods, 'code, 'core]
var $help_index topics 0
var $named name ['uniq, "help_index", "the help_index"]
var $named name_aliases []

root init_help_index
    topics = [];
.

public node_going_away
    var node;
    
    (> .perms(caller(), $help_node) <);
    node = sender();
    
    // do something intelligent with the text body as well
    links = links.del(node);
.

public find_help_node
    arg str;
    var node;
    
    for node in (topics) {
        if (node.match_name(str))
            return node;
    }
    return 0;
.



parent $help_root
object $help

var $root inited 1
var $root objname 'help
var $root created_on 796268969
var $root flags ['parameters, 'methods, 'code, 'core]

public default_help_node
    return $help_node_summary;
.



parent $help_root
parent $named
object $help_node

var $root child_index 5
var $root fertile 1
var $root inited 1
var $root objname 'help_node
var $root created_on 796268969
var $root flags ['parameters, 'methods, 'code, 'fertile, 'core]
var $named name ['uniq, "help_node", "the help_node"]
var $named name_aliases []
var $help_node links 0
var $help_node title 0
var $help_node body 0
var $help_node linked []
var $help_node indices 0
var $help_node linked_by []
var $help_node upnodes []
var $help_node downnodes []

root init_help_node
    linked_by = [];
    links = #[];
    
    // title = "";
    body = $ctext_frob.new([]);
    
    // upnodes = [];
    // downnodes = [];
.

root uninit_help_node
    var obj;
    
    for obj in (linked)
        (| obj.node_going_away() |);
    for obj in (indices)
        (| obj.node_going_away() |);
    indices = [];
    linked = [];
    links = #[];
    title = "";
    body = [];
.

public index_going_away
    (> .perms(caller(), $help_index) <);
    
    // ack, it is going away!  Not good, but we cannot do anything about it
    .del_index(sender());
.

public set_body
    arg new_body;
    var new_body, anchors, key, keys, values, value;
    
    (> .perms(sender()) <);
    
    // Compile a string into help ctext
    new_body = $cml2_compiler.compile_cml(new_body);
    body = new_body;
    
    //new_body = $help_evaluator.compile_cml(new_body);
    //body = $ctext_frob.new(new_body['result]);
    // If old anchors aren't in the new body, delete them.
    // old_anchors = links;
    //anchors = (| new_body['anchors] |) || #[];
    anchors = (| new_body.get_var('links) |) || #[];
    keys = anchors.keys();
    values = anchors.values();
    links = #[];
    for key in (keys)
        links = links.add(key, $object_lib.to_dbref(anchors[key]));
.

public body
    return body;
.

public links
    return links;
.

public add_link
    arg key, node;
    
    (> .perms(sender(), 'manager) <);
    links = links.add(key, node);
.

public del_link
    arg name;
    
    (> .perms(sender(), 'manager) <);
    if (name in links.keys())
        links = links.del(name);
.

public node_going_away
    var node;
    
    (> .perms(caller(), $help_node) <);
    node = sender();
    
    // do something intelligent with the text body as well
    links = links.del(node);
.

public eval_body
    return body.eval_ctext();
.

public add_index
    arg index;
    
    (> .perms(caller(), 'this) <);
    indices = indices.setadd(index);
.

public del_index
    arg index;
    
    (> .perms(caller(), 'this) <);
    indices = indices.setremove(index);
    
    // There can be unindexed topics.
    // if (!indices)
    //  .add_index($help_index_general);
.

public generate_body_as
    arg type;
    
    switch (type) {
        case "text/plain":
            //  $help_evaluator.eval_cml(  hsm, this isn't right...
        case "text/html":
    }
.

public set_title
    arg new_title;
    
    .perms(sender(), 'manager);
    title = new_title;
.

public title
    return title;
.

public accept_link
    linked_by = [@linked_by, sender()];
    return 1;
.

public _del_link
    //sender no longer links to us.
    if (sender() in linked_by)
        linked_by = linked_by.delete(sender() in linked_by);
.

public _set_link
    arg node, name;
    var linked_as;
    
    //Ask node if we can link to it.
    linked_as = node.accept_link(name);
    if (linked_as) {
        if (name in links.keys())
            links[name]._del_link(back_links[node]);
        links = links.add(name, node);
        back_links = back_links.add(linked_as);
    } else {
        throw(~refuse, "Node refused link");
    }
.

public delete_all
    var l;
    
    // delete all links to and from this node.
    if ("upnodes" in links.keys()) {
        for l in (links["upnodes"])
            l._del_downnode();
        links = links.del("upnodes");
    }
    if ("downnodes" in links.keys()) {
        for l in (links["downnodes"])
            l._del_upnode();
        links = links.del("downnodes");
    }
    for l in (links.keys())
        l._del_link();
.

public downnode_names
    var output, t;
    
    output = [];
    for t in (.downnodes())
        output = [@output, t.title()];
    return output;
.

public make_downnode
    arg name;
    var new;
    
    //make a new help node that is a downnode of this node.
    new = $help_node.spawn();
    new.set_name(name);
    .add_downnode(new);
    new.add_upnode(this());
.

public tell_help_node
    arg node;
    var out, len, clen, line, n;
    
    (> .perms(sender(), 'this) <);
    len = .linelen() % 2 ? .linelen() - 1 | .linelen();
    .tell((" " + node.name() + " ").center(len, "-"));
    .tell(node.eval_body());
    .tell("".pad(len, "-"));
    
    //if (node.upnodes())
    //    .tell("Up-nodes:   " + node.upnodes().map('name).to_english());
    //if (node.downnodes())
    //    .tell("Down-nodes: " + node.downnodes().map('name).to_english());
.

public node_name
    var name;
    
    if (this() == definer())
        return "";
    name = (.parents())[1].node_name();
    if (!name)
        return .name();
    return name + ": " + .name();
.

public html_node_name
    var name;
    
    if (this() == definer())
        return "";
    name = (.parents())[1].html_node_name();
    if (!name)
        return "<a href=\"/bin/help?" + .objname() + "\">" + .name() + "</a>";
    return name + ": <a href=\"/bin/help?" + .objname() + "\">" + .name() + "</a>";
.



parent $thing
parent $text
object $note

var $root child_index 31
var $root fertile 1
var $root inited 1
var $root objname 'note
var $root created_on 796268969
var $root flags ['parameters, 'methods, 'code, 'fertile, 'core]
var $text text []
var $located location $nowhere
var $located obvious 1
var $described prose []
var $messaged verbs #[["erase %this", ['erase_vrb, 'remote]], ["read|nread %this", ['read_vrb, 'remote]], ["write on %this", ['write_vrb, 'remote]], ["write at * on %this", ['write_at_vrb, 'remote]], ["write * on %this", ['write_str_vrb, 'remote]], ["copy from %this to *", ['copy_vrb, 'remote]], ["erase * on|from %this", ['erase_on_vrb, 'remote]]]
var $gendered gender $gender_neuter
var $named name ['uniq, "Generic Note", "the Generic Note"]
var $named name_aliases ["note"]
var $note seperator 0
var $public public []
var $has_commands remote #[["erase", [["erase", "*", "erase <this>", 'erase_cmd, #[[1, ['this, []]]]], ["erase", "* on|from *", "erase <string> on|from <this>", 'erase_on_cmd, #[[1, ['any, []]], [3, ['this, []]]]]]], ["read|nread", [["read|nread", "*", "read|nread <this>", 'read_cmd, #[[1, ['this, []]]]]]], ["write", [["write", "on *", "write on <this>", 'write_cmd, #[[2, ['this, []]]]], ["write", "at * on *", "write at <string> on <this>", 'write_at_cmd, #[[2, ['any, []]], [4, ['this, []]]]]]], ["copy", [["copy", "from * to *", "copy from <this> to <any>", 'copy_cmd, #[[2, ['this, []]], [4, ['any, []]]]]]]]

public add_text
    arg ntext, who, [args];
    
    // if at they should be an int defining where to insert.
    if (sender() != this()) {
        if (!.is_publicly('writable) && !.is_writable_by(sender()))
            throw(~perm, "Permission Denied.");
    }
    if (ntext) {
        if (ntext == 'aborted)
            return;
        if (args) {
            if (!(| .ins_lines(ntext, args[1]) |))
                who.tell("There are not that many lines in " + .name('ref) + ".");
        } else {
            .set_text(.text() + ntext);
        }
        who.tell("Line" + (ntext.length() == 1 ? "" | "s") + " added to " + .name('def) + ".");
    } else {
        who.tell("Text not added.");
    }
.

public seperator
    return type(seperator) == 'string ? seperator | "---";
.

root init_note
    .del_flag('parameters);
    .set_public('readable);
.

public init_for_core
    .perms(caller(), $sys);
.

public set_seperator
    arg newsep;
    
    .perms(sender(), 'manager);
    seperator = newsep;
.

public read_cmd
    arg cmdstr, cmd, [args];
    var who, text, prose;
    
    who = sender();
    if (!.is_publicly('readable) && !.trusts(who))
        return who.tell(.name() + " is not publicly readable!");
    who.tell(.name());
    who.tell(.prose());
    who.tell(.seperator());
    text = .text();
    if (cmd == "nread" && text)
        text = $list.numbered_text(text);
    who.tell(text ? text | ["", "(nothing)", ""]);
    who.tell(.seperator());
    who.tell("You finish reading " + .name() + ".");
.

public write_cmd
    arg cmdstr, cmd, [args];
    var who, line;
    
    who = sender();
    if (!.is_publicly('writable) && !.is_writable_by(who))
        return who.tell(.namef() + " is not publicly writable!");
    
    // because I'm odd lets do this all 1 one command.
    if (args.length() == 2) {
        line = "Now writing on " + .name('def);
        line = line + ", enter \".\" to finish and \"@abort\" to abort.";
    
        //      who.tell(line);
        .add_text(who.read(line), who);
    } else {
        args = args[1].explode();
        who.debug(args);
    }
.

public erase_on_cmd
    arg cmdstr, cmd, str, prep, this;
    var line, nline, who, len, oldline;
    
    who = sender();
    if (!.is_publicly('writable) && !.is_writable_by(who))
        return who.tell(.namef() + " is not publicly writable!");
    if (!str)
        return who.tell("You must erase either a line, line number, or all");
    catch any {
        if ($string.match_begin("all", str)) {
            .del_text();
    
            // if cmd is null, this method was called by an editor
            if (cmd)
                who.tell("All text cleared from " + .name('def) + ".");
        } else {
            if (str.explode().length() > 1)
                nline = toint((str.explode())[2]);
            else
                nline = toint(str);
            oldline = (.text())[nline];
            .del_nline(nline);
            line = "Line " + tostr(nline) + " (\"";
            len = who.linelen() - (25 + .name('def).length());
            line = line + $string.chop(oldline, len);
            line = line + "\") erased from " + .name('def);
            who.tell(line);
        }
    } with handler {
        switch (error()) {
            case ~range:
                who.tell("There are not that many lines in the text.");
            default:
                who.tell("Oops: " + traceback()[1][2]);
        }
    }
.

public erase_cmd
    arg cmdstr, cmd, this;
    var line, nline, len;
    
    if (!.is_publicly('writable) && !.is_writable_by(sender()))
        return sender().tell(.namef() + " is not publicly writable!");
    .del_text();
    
    // if cmd is null, this method was called by an editor originally.
    if (cmd)
        sender().tell("All text cleared from " + .name('def) + ".");
.

public write_str_cmd
    arg cmdstr, cmd, str, prep, this;
    
    if (!.is_publicly('writable) && !.is_writable_by(sender()))
        return .namef() + " is not publicly writable!";
    if (!str)
        return "Nothing to write on " + .name('def) + "!";
    .add_text([str], sender());
.

public write_at_cmd
    arg cmdstr, cmd, at, str, prep, this;
    var who, line, lines, syn;
    
    who = sender();
    if (!.is_publicly('writable) && !.is_writable_by(who))
        return who.tell(.namef() + " is not publicly writable!");
    syn = "`" + cmd + " at [line] <line number> on " + this + "`";
    str = str.explode();
    if (str[1] == "line")
        str = str.delete(1);
    line = $string.is_numeric(str[1]);
    if (!at)
        $parse_lib.tell_error("Unknown line \"" + str[1] + "\".", syn, who);
    lines = .text().length();
    if (line > lines + 1)
        $parse_lib.tell_error("There are only " + tostr(lines) + " lines!", syn, who);
    .add_text(who.read(), who, line);
.

public copy_cmd
    arg cmdstr, cmd, from, this, prep, where;
    var obj, what;
    
    // this method is outdated, needs to be rewritten.
    // it probably doesn't even work.
    if (!.is_writable_by(sender()) && !.is_publicly_readable())
        return sender().tell("You cannot read the text on " + .namef('ref) + ".");
    if ("." in where) {
        where = $parse_lib.reference(where);
        what = 1;
    } else {
        where = $parse_lib.reference(where, ",");
    }
    obj = sender().match_env_nice(where[1]);
    if (!obj.is_writable_by(sender()))
        return .tell("!  " + obj.namef('ref) + " is not owned by you.");
    if (obj.has_ancestor($note) && !where[2])
        obj.add_text(text, sender());
    
    // they want a method ref
    if (where[2]) {
        catch any {
            where = tosym(where[2]);
            if (what) {
                obj.(where)(text, sender());
                sender().tell("Text copied to " + $data_lib.unparse(obj) + "." + tostr(where) + "().");
            } else {
                if (!(where in obj.parameters()))
                    return .tell("!  parameter '" + tostr(where) + " not found");
                obj.eval([tostr(where) + " = " + $data_lib.unparse(.text())]);
            }
        } with handler {
            $brandon.tell_traceback(traceback());
            $parse_lib.tell_error(traceback()[1][2], "`" + cmd + " <obj>[.,<destination]`", sender());
        }
    }
    sender().tell("Text copied from " + .namef());
.

public description
    arg flags;
    
    return (> pass(flags) <) + ["You see some writing on the note, and may be able to read it..."];
.



parent $utilities
parent $text
parent $named
object $project

var $root objname 'project
var $root child_index 24
var $root created_on 799192659
var $root inited 1
var $root flags ['parameters, 'methods, 'code, 'core]
var $project list 0
var $text text []
var $named name ['uniq, "project", "the project"]
var $named name_aliases []

public list
    return list;
.

public change_list_to
    arg new_list;
    
    (> .perms(caller(), $projects) <);
    list = new_list;
.

public generate_html
    var out, tmp, line;
    
    out = ["<head><title>" + $motd.server_name() + " Project:" + .name() + "</title><center><img src=\"/tCD.gif\"><p><i><b><font size=+1>" + .name() + "</font></b></i></center></head>", "<body>", "<hr>"];
    tmp = .developers();
    
    // Developers
    line = "<b>Developer";
    if (tmp.length() > 1)
        line = line + "s:</b>";
    else
        line = line + ":</b>";
    out = out + [line + tmp.map('name).to_english() + "<br>"];
    
    // Status
    tmp = .list();
    if (!tmp)
        tmp = "No Status Set";
    else
        tmp = tostr(tmp).capitalize();
    out = out + ["<b>Status:</b> " + tostr(tmp), "<hr>"];
    return out + (.text() || []);
.

public developers
    return .writable().compress().setremove(this());
.



parent $text
parent $mail_root
object $mail_message

var $root child_index 88
var $root fertile 1
var $root inited 1
var $root objname 'mail_message
var $root created_on 796268969
var $root indestructable 1
var $root flags ['parameters, 'methods, 'code, 'fertile, 'core]
var $root quota_exempt -1
var $located location $the_pit
var $messaged verbs #[]
var $mail_message readers []
var $mail_message header #[]
var $mail_message delivered 0

public del_recipient: disallow_overrides
    arg whom;
    var rcpts;
    
    if (!$mail_lib.has_mail_perms(caller()))
        (> .perms(sender()) <);
    rcpts = header['rcpt].setremove(whom);
    if (!rcpts)
        .destroy();
    else
        header = header.add('rcpt, rcpts);
.

public add_recipient: disallow_overrides
    arg whom;
    var current;
    
    if (!$mail_lib.has_mail_perms(caller()))
        (> .perms(sender()) <);
    current = (| header['rcpt] |) || [];
    if ((| whom in current |))
        throw(~type, "Recipient is already in the list of recipients");
    header = header.add('rcpt, current.union([whom]));
.

public letter: disallow_overrides
    if (!$mail_lib.has_mail_perms(caller()))
        (> .perms(sender()) <);
    return .header().add('text, .text());
.

public readers: disallow_overrides
    if (!$mail_lib.has_mail_perms(caller()))
        (> .perms(sender()) <);
    return readers;
.

public did_read: disallow_overrides
    if (!$mail_lib.has_mail_perms(caller()))
        (> .perms(sender()) <);
    readers = [@readers, sender()];
.

public set_time: disallow_overrides
    arg time;
    
    if (!$mail_lib.has_mail_perms(caller()))
        (> .perms(sender()) <);
    header = header.add('time, time);
.

public set_recipients: disallow_overrides
    arg whom;
    
    if (!$mail_lib.has_mail_perms(caller()))
        (> .perms(sender()) <);
    header = header.add('rcpt, whom);
.

public set_subject: disallow_overrides
    arg what;
    
    if (!$mail_lib.has_mail_perms(caller()))
        (> .perms(sender()) <);
    header = header.add('subj, what);
.

public set_from: disallow_overrides
    arg whom;
    
    if (!$mail_lib.has_mail_perms(caller()))
        (> .perms(sender()) <);
    header = header.add('from, whom);
.

public add_reader: disallow_overrides
    arg who;
    
    if (!$mail_lib.has_mail_perms(caller()))
        (> .perms(sender()) <);
    readers = readers.union([who]);
.

public recipients: disallow_overrides
    if (!$mail_lib.has_mail_perms(caller()))
        (> .perms(sender()) <);
    return (| header['rcpt] |) || [$no_one];
.

public from: disallow_overrides
    if (!$mail_lib.has_mail_perms(caller()))
        (> .perms(sender()) <);
    return (| header['from] |) || $no_one;
.

public subject: disallow_overrides
    if (!$mail_lib.has_mail_perms(caller()))
        (> .perms(sender()) <);
    return (| header['subj] |) || "<none>";
.

public time: disallow_overrides
    if (!$mail_lib.has_mail_perms(caller()))
        (> .perms(sender()) <);
    return (| header['time] |) || 0;
.

public format: disallow_overrides
    var output, names, o, h;
    
    if (!$mail_lib.has_mail_perms(caller()))
        (> .perms(sender()) <);
    h = .header();
    output = ["From:    " + h['from].namef('ref)];
    names = h['rcpt].omap($mail_lib, 'mail_name).to_english();
    output = [@output, "To:      " + names];
    output = [@output, "When:    " + $time.date(h['time])];
    output = [@output, "Subject: " + h['subj]];
    output = [@output, "---", @.text(), "---"];
    return output;
.

public header: disallow_overrides
    var h, d;
    
    (> .perms(sender()) <);
    h = #[['from, $no_one], ['rcpt, [$no_one]], ['subj, "<none>"], ['time, 0]];
    for d in (header)
        h = h.add(@d);
    return h;
.

root uninit_mail_message
    var r;
    
    for r in (.recipients())
        (| r.del_mail(this(), (| .from() || this() |)) |);
    header = #[];
    readers = [];
.

root init_mail_message
    header = #[];
    readers = [];
    .set_flags([]);
.

public has_read: disallow_overrides
    arg who;
    
    return who in readers;
.

public text
    return (> pass() <) || ["", "(no message)", ""];
.

public send
    arg [recips];
    var r, valid, invalid;
    
    if (!$mail_lib.has_mail_perms(caller()))
        (> .perms(sender()) <);
    if (delivered)
        throw(~delivered, "This mail has already been delivered.");
    if (!recips)
        recips = .recipients();
    if (recips.length() == 1 && recips[1] == $no_one)
        throw(~norcpt, "No recipients specified.");
    valid = [];
    invalid = [];
    for r in (recips) {
        if (r.has_ancestor($mail_list))
            valid = [@valid, r];
        else
            invalid = [@invalid, r];
    }
    if (invalid)
        throw(~invrcpt, "Invalid mail recipients: " + invalid.map('name), invalid);
    recips = valid;
    invalid = [];
    
    // ok, now that we have that cleared up, lets set pertinent info...
    .set_time(time());
    .set_from(sender());
    
    // and now to finalize the recipients
    for r in (recips) {
        if (r.list_is_sendable_by(sender())) {
            r.add_mail();
            .add_recipient(r);
        } else {
            invalid = [@invalid, r];
        }
    }
    delivered = 1;
    return invalid;
.

public new_mail
    var new;
    
    if (!$mail_lib.has_mail_perms(caller()))
        (> .perms(sender()) <);
    if (definer() != $mail_message)
        throw(~perm, "Only spawn mail from $mail_message.");
    new = .spawn();
    new.add_writer(sender());
    new.change_manager(new);
    return new;
.



parent $frob
parent $messaged
object $event_frob

var $root inited 1
var $root objname 'event_frob
var $root created_on 796268969
var $root flags ['parameters, 'methods, 'code, 'core]
var $event_frob type 'generic
var $messaged messages #[]

public new
    arg [args];
    
    .perms(caller(), $event_handler);
    
    // we still don't know what we want
    //  if (type(location) != 'dbref)
    //    throw(~type, "The location must be given as a objname");
    // return <this(), #[['actor, sender()], ['action, action], ['what, what], ['where, location], ['direction, direction]]>;
    return (<this(), [sender(), @args]>);
.

public dispatch
    arg dict;
    
    dict['where].event((<this(), dict>));
.

public set_event_type
    arg newtype;
    
    .perms(sender());
    if (type(newtype) != 'symbol)
        throw(~type, "New event types must be symbols.");
    type = newtype;
.

public type
    arg frob;
    
    return type;
.

public process
    arg [args];
    
.



parent $gender
object $gender_female

var $root inited 1
var $root objname 'gender_female
var $root created_on 796268969
var $root flags ['parameters, 'methods, 'code, 'core]
var $gender cgender_name "Female"
var $gender gender_name "female"
var $gender pronouns #[['pr, "herself"], ['pp, "her"], ['po, "her"], ['ps, "she"], ['pq, "hers"], ['prc, "Herself"], ['ppc, "Her"], ['poc, "Her"], ['psc, "She"], ['pqc, "Hers"]]
var $gender number 'singular
var $gender context ["herself", "her", "her", "she", "hers", "Herself", "Her", "Her", "She", "Hers"]
var $named name ['prop, "female", "female"]



parent $gender
object $gender_male

var $root inited 1
var $root objname 'gender_male
var $root created_on 796268969
var $root flags ['parameters, 'methods, 'code, 'core]
var $gender cgender_name "Male"
var $gender gender_name "male"
var $gender pronouns #[['pr, "himself"], ['pp, "his"], ['po, "him"], ['ps, "he"], ['pq, "his"], ['prc, "Himself"], ['ppc, "His"], ['poc, "Him"], ['psc, "He"], ['pqc, "His"]]
var $gender number 'singular
var $gender context ["himself", "his", "him", "he", "his", "Himself", "His", "Him", "He", "His"]
var $named name ['prop, "male", "male"]



parent $gender
object $gender_plural

var $root trusted []
var $root inited 1
var $root objname 'gender_plural
var $root created_on 796268969
var $root flags ['parameters, 'methods, 'code, 'core]
var $gender cgender_name "Plural"
var $gender gender_name "plural"
var $gender pronouns #[['pr, "themselves"], ['pp, "their"], ['po, "them"], ['ps, "they"], ['pq, "theirs"], ['prc, "Themselves"], ['ppc, "Their"], ['poc, "Them"], ['psc, "They"], ['pqc, "Theirs"]]
var $gender number 2
var $gender context ["themselves", "their", "them", "they", "theirs", "Themselves", "Their", "Them", "They", "Theirs"]
var $named name ['prop, "plural", "plural"]



parent $gender
object $gender_neuter

var $root inited 1
var $root objname 'gender_neuter
var $root created_on 796268969
var $root flags ['parameters, 'methods, 'code, 'core]
var $gender cgender_name "Neuter"
var $gender gender_name "neuter"
var $gender pronouns #[['pr, "itself"], ['pp, "its"], ['po, "it"], ['ps, "it"], ['pq, "its"], ['prc, "Itself"], ['ppc, "Its"], ['poc, "It"], ['psc, "It"], ['pqc, "Its"]]
var $gender number 'singular
var $gender context ["itself", "its", "it", "it", "its", "Itself", "Its", "It", "It", "Its"]
var $named name ['prop, "neuter", "neuter"]



parent $libraries
object $dictionary

var $root inited 1
var $root objname 'dictionary
var $root created_on 796268969
var $root flags ['parameters, 'methods, 'code, 'core]

public map_method
    arg ls, what;
    var x, dict;
    
    // args[1] == list of objects
    // args[2] == symbol for method on objects
    // it will create a dictionary out of the two.
    dict = #[];
    
    // Get list's method(whatever) and add it to the dictionary
    for x in [1 .. ls.length()]
        dict = dict.add(ls[x], ls[x].(what)());
    return dict;
.

public merge
    arg [args];
    var x, dict, z, tule, dz, axz, keys;
    
    // merges all dictionaries into a single one, if they have the same key's --
    // basing off of args[1] (this should be the longest list (i know, bad Lynx).
    dict = args[1];
    keys = args[1].keys();
    for x in [2 .. args.length()] {
        for z in (keys) {
            dz = dict[z];
            axz = args[x][z];
            if (type(dict[z]) == 'list)
                tule = dz;
            else
                tule = [dz];
            tule = [@tule, axz];
            dict = dict.add(z, tule);
        }
    }
    return dict;
.

public to_list
    arg dict;
    var list, x, k;
    
    // merges into an associated list.
    k = dict.keys();
    list = [];
    for x in (k)
        list = [@list, [x, dict[x]]];
    return list;
.

public merge_to_list
    arg [args];
    var x, dict, z, tule, dz, axz, keys, list;
    
    // merges all dictionaries into a single list, where each related key
    // is merged with all it's other values as a subrange
    // basing off of args[1] (this should be the longest list (i know, bad Lynx).
    dict = .merge(@args);
    list = [];
    for z in (dict.keys())
        list = [@list, dict[z]];
    return list;
.

public union
    arg dict1, dict2;
    var key;
    
    // like union() but for dictionaries.  adds any keys from dict2 that don't
    // already exist in dict1 to dict1 and returns the result.  Order of keys in
    // result is not guaranteed.
    for key in (dict1)
        dict2 = dict2.add(key[1], key[2]);
    return dict2;
.

public values
    arg dict;
    var list, x, k;
    
    // returns values same as dict_keys() returns keys.
    k = dict.keys();
    list = [];
    for x in (k)
        list = [@list, dict[x]];
    return list;
.

public replace
    arg dict, key, value;
    
    dict = (> dict.del(key) <);
    dict = (> dict.add(key, value) <);
    return dict;
.

public apply
    arg tdict, list;
    var x;
    
    // Apply a translation-dict to a list
    for x in [1 .. list.length()] {
        catch ~keynf {
            list = list.replace(x, tdict[list[x]]);
        }
    }
    return list;
.

public apply_to_keys
    arg tdict, dict;
    var x, newdict;
    
    // Apply a t-dict to the keys of a dict
    newdict = #[];
    for x in (dict) {
        catch ~keynf {
            x = x.replace(1, tdict[x[1]]);
        }
        newdict = newdict.add(@x);
    }
    return newdict;
.

public apply_to_values
    arg tdict, dict;
    var x, newdict;
    
    // Apply a t-dict to the values of a dict
    newdict = #[];
    for x in (dict) {
        catch ~keynf {
            x = x.replace(2, tdict[x[2]]);
        }
        newdict = newdict.add(@x);
    }
    return newdict;
.

public invert
    arg dict;
    var inverted, x;
    
    // Invert a dict (keys<->values)
    inverted = #[];
    for x in (dict.invert())
        inverted = inverted.add(dict[x], x);
    return inverted;
.

public add_elem
    arg dict, key, elem;
    var value;
    
    // same as old dict_add_elem
    value = (| dict[key] |);
    if (type(value) != 'list && type(value) != 'error)
        throw(~type, "Value for key " + $data_lib.unparse(key) + " (" + $data_lib.unparse(value) + ") is not a list.");
    if (value)
        value = [@value, elem];
    else
        value = [elem];
    return dict.add(key, value);
.

public del_elem
    arg dict, key, elem;
    var value;
    
    value = (| dict[key] |);
    if (type(value) != 'list && type(value) != 'error)
        throw(~type, "Value for key " + $data_lib.unparse(key) + " (" + $data_lib.unparse(value) + ") is not a list.");
    value = value.setremove(elem);
    if (!value)
        return dict.del(key);
    return dict.add(key, value);
.

public add
    arg [args];
    
    return (> dict_add(@args) <);
.

public del
    arg [args];
    
    return (> dict_del(@args) <);
.

public keys
    arg dict;
    
    return (> dict_keys(dict) <);
.

public add_elem_union
    arg dict, key, elem;
    var value;
    
    value = (| dict[key] |);
    if (value)
        value = value.union([elem]);
    else
        value = [elem];
    return dict.add(key, value);
.

public contains
    arg [args];
    
    return (> dict_contains(@args) <);
.



parent $libraries
object $time_root

var $root inited 1
var $root objname 'time_root
var $root created_on 796268969
var $root flags ['parameters, 'methods, 'code, 'core]
var $time_root secs_per_year 31536000
var $time_root mins_per_hour 60
var $time_root secs_per_week 604800
var $time_root secs_per_day 86400
var $time_root created_on 758611955
var $time_root hours_per_day 24
var $time_root days_per_year 365
var $time_root year_begin 0
var $time_root secs_per_min 60
var $time_root days [""]
var $time_root months #[["Jan", "January"], ["Feb", "February"], ["Mar", "March"], ["Apr", "April"], ["May", "May"], ["Jun", "June"], ["Jul", "July"], ["Aug", "August"], ["Sep", "September"], ["Oct", "October"], ["Nov", "November"], ["Dec", "December"]]
var $time_root standard 0
var $time_root secs_per_hour 3600

public init_time
    .perms(caller(), $root);
    secs_per_min = 60;
    secs_per_hour = 3600;
    secs_per_day = 86400;
    secs_per_week = 604800;
    secs_per_year = 31536000;
    created_on = 0;
    mins_per_hour = 60;
    hours_per_day = 24;
    days_per_year = 365;
    year_begin = 0;
    days = [""];
    months = [""];
    standard = 0;
.

public time
    return .convert();
.

public ctime
    arg [time];
    
    time = [@time, .time()][1];
    if (!standard)
        return "still working on this";
    else
        return ctime(time);
.

public hr_min_sec
    arg [time];
    
    // returns a list: ["hh", "mm", "ss"]
    return [tostr(.hour()), tostr(.minute()), tostr(.second())];
.

public hour
    arg [time];
    
    // either send a pre-converted time, or nothing.
    time = time ? time[1] | standard ? time() | .convert();
    return time % secs_per_year % secs_per_day / secs_per_hour;
.

public convert
    arg [time];
    
    // converts time into whatever time based off created_on
    return [@time, time()][1] - created_on;
.

public minute
    arg [time];
    
    // either send a pre-converted time, or nothing.
    time = time ? time[1] | standard ? time() | .convert();
    return time % secs_per_year % secs_per_day % secs_per_hour / secs_per_min;
.

public second
    arg [time];
    
    // either send a pre-converted time, or nothing.
    time = time ? time[1] | standard ? time() | .convert();
    return time % secs_per_year % secs_per_day % secs_per_hour % secs_per_min;
.

public year
    arg [time];
    
    // either send a pre-converted time, or nothing.
    // returns years since 'created_on;
    // prolly not right.
    time = time ? time[1] | standard ? time() | .convert();
    return time / secs_per_year + year_begin;
.

public day
    arg [time];
    
    // either send a pre-converted time, or nothing.
    // returns days since 'created_on;
    // prolly not right.
    time = time ? time[1] | standard ? time() | .convert();
    return time / secs_per_day;
.

public minute_str
    arg [args];
    
    // will call $integer.int_to_name
    return 'null;
.

public hour_str
    arg [args];
    
    // will call $integer.int_to_name
    return 'null;
.

public second_str
    arg [args];
    
    // will call $integer.int_to_name
    return 'null;
.

public day_str
    arg [time];
    
    time = [@time, .time()][1];
    return tostr(.day());
.

public months
    return months;
.

public days
    return days;
.

public secs_per_min
    return secs_per_min;
.

public secs_per_hour
    return secs_per_hour;
.

public secs_per_day
    return secs_per_day;
.

public secs_per_week
    return secs_per_week;
.

public secs_per_year
    return secs_per_year;
.



parent $libraries
object $buffer

var $root trusted []
var $root inited 1
var $root objname 'buffer
var $root created_on 796268969
var $root flags ['parameters, 'methods, 'code, 'core]

public to_list
    arg buf;
    var idx, list;
    
    list = [];
    for idx in [1 .. buf.length()]
        list = list + [buf.retrieve(idx)];
    return list;
.

public from_list
    arg list;
    var buf, x;
    
    buf = `[];
    for x in [1 .. list.length()]
        buf = buf.add(list[x]);
    return buf;
.

public length
    arg buffer;
    
    return (> buffer_len(buffer) <);
.

public retrieve
    arg buffer, position;
    
    return (> buffer_retrieve(buffer, position) <);
.

public append
    arg buffer, buffer_2;
    
    return (> buffer_append(buffer, buffer_2) <);
.

public replace
    arg buffer, position, value;
    
    return (> buffer_replace(buffer, position, value) <);
.

public add
    arg buffer, value;
    
    return (> buffer_add(buffer, value) <);
.

public truncate
    arg buffer, position;
    
    return (> buffer_truncate(buffer, position) <);
.

public tail
    arg buffer, position;
    
    return (> buffer_tail(buffer, position) <);
.

public to_string
    arg buffer;
    
    return (> buffer_to_string(buffer) <);
.

public to_strings
    arg [args];
    
    return (> buffer_to_strings(@args) <);
.

public from_string
    arg string;
    
    return (> buffer_from_string(string) <);
.

public from_strings
    arg strings, [seperator];
    
    return (> buffer_from_strings(strings, @seperator) <);
.



parent $libraries
object $string

var $root inited 1
var $root objname 'string
var $root created_on 796268969
var $root flags ['parameters, 'methods, 'code, 'core]
var $string alphabet "abcdefghijklmnopqrstuvwxyz"
var $string numbers "1234567890"
var $string non_alphanumeric "!@#$%^&*()_+-=~`'{}[]|/?\"\\,.<>;: "

public left
    arg str, width, [fchar];
    
    // will NOT chop off 'str' if it is longer than width, use pad() for that.
    if (fchar)
        return str + (str.length() < width ? "".pad(width - str.length(), fchar[1]) | "");
    else
        return str + (str.length() < width ? "".pad(width - str.length()) | "");
.

public fill
    arg n, [args];
    var fill, x;
    
    // same as pad("", n, [args]);
    fill = [@args, " "][1];
    return "".pad(n, fill);
.

public center
    arg text, len, [args];
    var lfill, rfill, textlen, padlen;
    
    // args[1] == string to center
    // args[2] == integer of width to center in
    // args[3] <op> == what to fill the left|right side with.
    // args[4] <op> == what to fill the right side with.
    lfill = args.length() >= 1 && args[1] || " ";
    rfill = args.length() >= 2 ? args[2] | lfill == " " ? "" | lfill;
    textlen = text.length();
    padlen = (len - textlen) / 2;
    if (textlen < len)
        return .fill(padlen, lfill) + text + (rfill ? .fill(padlen, rfill) | "");
    else
        return len > 0 ? text | text.pad(len);
.

public trim
    arg string, [args];
    var rl, chars, type;
    
    // remove leading and trailing characters.
    // if args includes a string, it takes that as the strip string.
    // args can also include symbols of what edge to trim: 'left 'right (or both)
    // left and right defaults on.
    rl = [];
    while (args) {
        type = type(args[1]);
        if (type == 'string)
            chars = args[1];
        else if (type == 'symbol)
            rl = [@rl, args[1]];
        args = args.subrange(2);
    }
    if (!chars)
        chars = " ";
    if (!rl)
        rl = ['left, 'right];
    if ('left in rl) {
        // strip from left
        while (string && string[1] in chars)
            string = string.subrange(2);
    }
    if ('right in rl) {
        // strip from right
        while (string && string[string.length()] in chars)
            string = string.subrange(1, string.length() - 1);
    }
    return string;
.

public to_list
    arg str, [sep];
    var result, list;
    
    // separate a string into a list of strings, breaking wherever 'sep' appears.
    // if not provided, sep defaults to a comma.
    // One word of warning.  sep should not contain an asterisk.  If it does,
    // this routine will separate the string oddly, most likely losing bits.
    if (!str)
        return [];
    sep = "*" + (sep ? sep[1] | ",") + "*";
    list = [];
    while (1) {
        result = $string.match_pattern(sep, str);
        if (result) {
            list = list + [result[1]];
            str = result[2];
        } else {
            return list + [str];
        }
    }
.

public right
    arg str, width, [fchar];
    
    // will not chop off 'str' if it is longer than width (unlike pad())
    if (fchar)
        return "".pad(width - str.length(), fchar[1]) + str;
    else
        return "".pad(width - str.length()) + str;
.

public alphabet
    return alphabet;
.

public numbers
    return numbers;
.

public capitalize
    arg string;
    
    // Capitalizes the first character of a word.
    return string[1].uppercase() + string.subrange(2);
.

public is_numeric
    arg string;
    
    return toint(string) || string == "0";
.

public a_or_an
    arg string;
    
    if (string[1].lowercase() in "aeiou")
        return "an";
    return "a";
.

public strip
    arg string, strip;
    var new_str, char;
    
    // strips all of "strip" characters from the string
    // if "strip" is -1 it will use .non_alphanumeric()
    if (type(string) != 'string || type(strip) != 'string && strip != -1)
        throw(~type, "First argument must be a string, second can be -1");
    new_str = "";
    if (strip == -1)
        new_str = non_alphanumeric;
    for char in [1 .. string.length()] {
        if (!(string[char] in strip))
            new_str = new_str + string[char];
    }
    return new_str;
.

public non_alphanumeric
    return non_alphanumeric;
.

public chop
    arg str, len, [end];
    
    // chops string off end.length() characters before len and appends len
    end = [@end, "..."][1];
    if (str.length() < len)
        return str;
    if (str.length() < end.length())
        return str;
    return str.pad(len - end.length()) + end;
.

public replace
    arg [args];
    
    return (> strsub(@args) <);
.

public explode_english_list
    arg line, [opts];
    var x, output, tmp;
    
    // explodes an english list ("foo, bar and zoo").
    line = line.explode(",");
    output = [];
    for x in (line) {
        x = .trim(x);
        if ((| x.subrange(1, 3) |) == "and")
            output = [@output, .trim(x.subrange(4))];
        else
            output = [@output, x];
    }
    
    // check the last element, if they didn't specify  'noand
    if (!('noand in opts)) {
        line = output[output.length()].explode();
        tmp = "";
        for x in [1 .. line.length()] {
            if (line[x] == "and") {
                output = output.delete(output.length());
                if (tmp)
                    output = [@output, tmp];
                tmp = $list.to_string(line.subrange(x + 1));
                if (tmp)
                    output = [@output, tmp];
    
                // only bother with the first "and"
                break;
            }
            tmp = tmp + (tmp ? " " | "") + line[x];
        }
    }
    return output;
.

public explode_delimited
    arg str, left, right;
    var pattern, parsed, matched, match_num, match_result;
    
    // parse str looking for anything surrounded by left and right
    // ;$string.explode_delimited("foo%[bar]baz", "%[", "]")
    // => [["foo", 1, "baz"], ["bar"]]
    pattern = "*" + left + "*" + right + "*";
    parsed = [];
    matched = [];
    match_num = 0;
    while (str) {
        match_result = $string.match_pattern(pattern, str);
        if (match_result) {
            match_num = match_num + 1;
            parsed = [@parsed, match_result[1], match_num];
            matched = [@matched, match_result[2]];
            str = match_result[3];
        } else {
            parsed = [@parsed, str];
            str = "";
        }
    }
    return [parsed, matched];
.

public wrap_line
    arg string, length, [stuff];
    var output, cutoff, firstline, prefix;
    
    // takes string and wraps it by words, compared to length, breaks with \n
    prefix = [@stuff, ""][1];
    firstline = [@stuff, 0, 0][2];
    output = "";
    if (firstline)
        string = prefix + string;
    while (string.length() > length) {
        cutoff = .rindex(string.subrange(1, length), " ");
        output = output + "\\n" + string.subrange(1, cutoff - 1);
        string = prefix + string.subrange(cutoff + 1);
    }
    return output + "\\n" + string;
.

public rindex
    arg string, index;
    var loc, rest;
    
    // returns the first occurance of index starting from the end of the string,
    // and moving to the beginning.
    loc = index in string;
    rest = loc && string.subrange(loc + 1);
    while (loc && index in rest) {
        loc = loc + (index in rest);
        rest = loc && string.subrange(loc + 1);
    }
    return loc;
.

public match_sub_tag
    arg string, tag;
    var x, expl, output, match, matches;
    
    // matches a string between 'tag' and " " in a larger string against
    // the sender's environment.  If a match is found it subs the match.name
    // with the string, otherwize it lets it pass through with the tag, ie:
    // .match_sub_tag("this test #of something #note or other");
    // => "this test #of something Note of sorts or other"
    // where the note is in the sender's environment.
    expl = .explode_delimited(string + " ", tag, " ");
    matches = expl[2];
    expl = expl[1];
    output = "";
    for x in (expl) {
        if (type(x) == 'integer) {
            match = (| sender().match_environment(matches[x]) |);
            if (match)
                output = output + match.namef() + " ";
            else
                output = output + tag + matches[x] + " ";
        } else {
            output = output + x;
        }
    }
    return output.subrange(1, output.length() - 1);
.

public search_pat
    arg pat, text, [start_at];
    var line, match_result, type;
    
    line = 1;
    type = [@start_at, 'pattern, 'pattern][2] == 'pattern ? 'match_pattern | 'match_regexp;
    if (start_at) {
        line = start_at[1];
        start_at = [@start_at, 1, 1][2];
        match_result = pat.(type)(text[line].subrange(line));
        if (match_result != 0) {
            if (type == 'match_pattern) {
                pat = $string.pat_sub(pat, match_result);
                return [line, start_at + pat in text[line].subrange(start_at)];
            } else {
                return [line, start_at + match_result[1][1]];
            }
        }
        line = line + 1;
    }
    while (line <= text.length()) {
        match_result = pat.(type)(text[line]);
        if (match_result != 0) {
            if (type == 'pattern) {
                pat = $string.pat_sub(pat, match_result);
                return [line, pat in text[line]];
            } else {
                return [line, match_result[1][1]];
            }
        }
        line = line + 1;
    }
    throw(~strnf, "String not found in text.");
.

public pat_sub
    arg pat, subs;
    var wc_idx;
    
    // wc_idx == wildcard index
    while (subs) {
        wc_idx = "*" in pat;
        if (wc_idx == 1)
            pat = subs[1] + pat.subrange(2);
        else if (wc_idx == pat.length())
            pat = pat.subrange(1, wc_idx - 1) + subs[1];
        else
            pat = pat.subrange(1, wc_idx - 1) + subs[1] + pat.subrange(wc_idx + 1);
        subs = subs.delete(1);
    }
    return pat;
.

public is_boolean
    arg str;
    
    if ($string.match_begin("yes", str) || $string.match_begin("true", str) || str == "1")
        return 1;
    else if ($string.match_begin("no", str) || $string.match_begin("false", str) || str == "0")
        return 0;
    else
        return -1;
.

public parse_template
    arg str;
    var index, out;
    
    out = (str.explode(" *"))[1];
    
    // index = "?" in str;
    // if (index) {
    //     out = uppercase(str.subrange(1, index - 1));
    //     out = out + "?" + str.subrange(index + 1);
    // } else {
    //     out = uppercase(out);
    // }
    return out;
.

public repeat
    arg string, times;
    var t, out;
    
    // repeats <string> <times> times
    if (type(string) != 'string)
        throw(~type, "The first agrument must be a string.");
    if (type(times) != 'integer || times < 0)
        throw(~type, "The second agrument must be a non-negatiive integer.");
    out = "";
    for t in [1 .. times]
        out = out + string;
    return out;
.

public explode
    arg [args];
    
    return (> explode(@args) <);
.

public match_template
    arg template, string;
    
    return (> match_template(template, string) <);
.

public find_next
    arg str, choices;
    var t, first, pos;
    
    //Returns the index of the first string in choices to appear.
    //Returns str.length() if none are in str.
    first = str.length() + 1;
    for t in (choices) {
        pos = t in str;
        if (pos && pos < first)
            first = pos;
    }
    return first;
.

public split_on_next
    arg str, choices;
    var pos, pre, post;
    
    // splits str around whichever choice appears first.
    pos = $string.find_next(str, choices);
    pre = (| str.subrange(1, pos - 1) |) || "";
    post = (| str.subrange(pos + 1) |) || "";
    return [pre, (| str[pos] |) || "", post];
.

public explode_template_word
    arg template;
    var t, x, idx, out, new;
    
    // this only explodes single word templates
    template = template.explode("|");
    out = [];
    for t in (template) {
        idx = "?" in t;
        if (idx) {
            t = t.strip("?");
            new = t.subrange(1, idx - 1);
            out = [@out, new];
            for x in [idx .. t.length()] {
                new = new + t[x];
                out = [@out, new];
            }
        } else {
            out = [@out, t];
        }
    }
    return out;
.

public match_pattern
    arg pattern, string;
    
    return (> match_pattern(pattern, string) <);
.

public match_regexp
    arg [args];
    
    return (> match_regexp(@args) <);
.

public to_number
    arg str;
    
    if (str.is_numeric())
        return toint(str);
    throw(~nonum, "\"" + str + "\" is not a number.");
.

public toliteral
    arg [args];
    
    return (> toliteral(@args) <);
.

public last
    arg str;
    
    return str[str.length()];
.

public explode_list
    arg str;
    
    if ("," in str)
        return str.explode_english_list();
    else
        return str.explode();
.

public onespace
    arg str;
    var c, l, out;
    
    l = "";
    out = "";
    for c in [1 .. str.length()] {
        pause();
        if (str[c] != " " || str[c] != l)
            out = out + str[c];
        l = str[c];
    }
    return out;
.

public find_next_escaped
    arg str, choices;
    var t, first, pos, good, p, start;
    
    //Returns the index of the first string in choices to appear.
    //If 
    //Returns str.length() if none are in str.
    first = str.length() + 1;
    for t in (choices) {
        pos = str.find_escaped(t);
        if (pos < first)
            first = pos;
    }
    return first;
.

public split_on_next_escaped
    arg str, choices;
    var pos, pre, post;
    
    // splits str around whichever choice appears first.
    pos = str.find_next_escaped(choices);
    pre = (| str.subrange(1, pos - 1) |) || "";
    post = (| str.subrange(pos + 1) |) || "";
    return [pre, (| str[pos] |) || "", post];
.

public find_escaped
    arg str, char;
    var good, start, pos, p;
    
    good = 0;
    start = 0;
    while (!good && start < str.length()) {
        pos = (char in str.subrange(start + 1)) + start;
        good = 1;
        if (pos > start) {
            p = pos - 1;
            while (p > 0 && str[p] == "\\") {
                good = good ? 0 | 1;
                p = p - 1;
            }
        }
        if (good)
            return pos;
        else
            start = pos;
    }
.

public explode_quoted
    arg str;
    var out, result;
    
    out = [];
    while (str) {
        result = .match_pattern("*\"*\"*", str);
        if (result) {
            out = [@out, @result[1].explode(), result[2].trim()];
            str = result[3];
        } else {
            out = [@out, @str.explode()];
            str = "";
        }
    }
    return out;
.

public strip_article
    arg str;
    var articles, a, r;
    
    articles = ["a ", "an ", "the "];
    for a in (articles) {
        if (r = $string.match_regexp("^" + a + "[ ]*(.*)", str))
            return str.subrange(@r[2]);
    }
    return str;
.

public rindexc
    arg str, c;
    var i;
    
    // same as rindex, but only with a single character, faster.
    i = str.length();
    while (i) {
        if (str[i] == c)
            return i;
        i = i - 1;
    }
    return 0;
.

public echo
    arg str;
    
    return str;
.

public sub
    arg regexp, string;
    var match, m, complete, out;
    
    match = $string.match_regexp(regexp, string);
    if (!match)
        return 0;
    complete = match[1];
    out = [];
    for m in (match.delete(1)) {
        if (!m[1])
            break;
        out = out + [string.subrange(@m)];
    }
    return out || string.subrange(@complete);
.

public unquote
    arg str;
    
    if (str && str[1] == "\"" && str[str.length()] == "\"")
        return str.subrange(2, str.length() - 2);
    return str;
.

public to_buffer
    arg string;
    
    return (> $buffer.from_string(string) <);
.

public pad
    arg [args];
    
    return (> pad(@args) <);
.

public valid_method_name
    arg str;
    
    return .strip_others(str, alphabet + numbers + "_").length() == str.length();
.

public strip_others
    arg string, valid;
    var new_str, char;
    
    // strips all but "strip" characters from the string
    new_str = "";
    for char in [1 .. string.length()] {
        if (string[char] in valid)
            new_str = new_str + string[char];
    }
    return new_str;
.

public wrap_lines
    arg string, length, [stuff];
    var output, cutoff, firstline, prefix;
    
    // takes string and wraps it by words, compared to length, returns a list.
    prefix = [@stuff, ""][1];
    firstline = [@stuff, 0, 0][2];
    output = [];
    if (firstline)
        string = prefix + string;
    while (string.length() > length) {
        cutoff = .rindex(string.subrange(1, length), " ");
        output = [@output, string.subrange(1, cutoff - 1)];
        string = prefix + string.subrange(cutoff + 1);
    }
    return [@output, string];
.

public length
    arg str;
    
    return (> strlen(str) <);
.

public subrange
    arg [args];
    
    return (> substr(@args) <);
.

public match_begin
    arg [args];
    
    return (> match_begin(@args) <);
.

public crypt
    arg [args];
    
    return (> crypt(@args) <);
.

public uppercase
    arg string;
    
    return (> uppercase(string) <);
.

public lowercase
    arg string;
    
    return (> lowercase(string) <);
.

public compare
    arg str1, str2;
    
    return (> strcmp(str1, str2) <);
.

public format
    arg format, [args];
    
    return (> strfmt(format, args) <);
.

public to_symbol
    arg [args];
    
    return (> tosym(@args) <);
.



parent $libraries
object $command_lib

var $root inited 1
var $root objname 'command_lib
var $root created_on 796268969
var $root flags ['parameters, 'methods, 'code, 'core]
var $command_lib argument_types [["any", 'any], ["text", 'any], ["string", 'any], ["object", 'object], ["thing", 'thing], ["user", 'user], ["descendant of *", 'descendant], ["this", 'this], ["number", 'number], ["object reference", 'objref]]

public argument_types
    return argument_types;
.

public get_argument_type
    arg type;
    var x, m;
    
    for x in [1 .. argument_types.length()] {
        m = $string.match_pattern(argument_types[x][1], type);
        if (type(m) == 'list) {
            switch (argument_types[x][2]) {
                case 'descendant:
                    if (!m)
                        throw(~type, "Argument type \"descendant of\" must have an object.");
                    return ['descendant, [(> $object_lib.to_dbref(m[1]) <)]];
                case 'thing:
                    return ['descendant, [$thing]];
                default:
                    return [argument_types[x][2], []];
            }
        }
    }
    throw(~command, "Invalid command argument type \"" + type + "\"");
.

public handle_shortcut_fields
    arg subs, fields;
    var subbed_list, elem;
    
    subbed_list = [];
    for elem in (subs) {
        if (type(elem) == 'string)
            subbed_list = [@subbed_list, elem];
        else if (type(elem) == 'integer)
            subbed_list = [@subbed_list, (> fields[elem] <)];
        else
            throw(~type, "Substitution element is of wrong type.");
    }
    return subbed_list;
.

public validate_command_template
    arg str;
    var cmd, tmp, loc, types, part, relations;
    
    tmp = str.explode_delimited("<", ">");
    loc = [];
    types = tmp[2];
    tmp = tmp[1];
    cmd = [];
    relations = #[];
    for part in (tmp) {
        if (type(part) == 'string)
            cmd = cmd + part.explode();
        else
            cmd = [@cmd, part];
    }
    
    // clean
    for part in [1 .. cmd.length()] {
        if (type(cmd[part]) == 'string) {
            cmd = cmd.replace(part, cmd[part].trim());
        } else {
            relations = relations.add(part - 1, (> .get_argument_type(types[cmd[part]]) <));
            cmd = cmd.replace(part, "*");
        }
    }
    cmd = [cmd[1], cmd.subrange(2).to_string()];
    return [cmd, relations];
.

public parse_relation
    arg str, template;
    var part, out, list, index;
    
    // "foo* *", "foo $2 bar $1" => ["foo* *", ["foo", 2, "bar", 1]]
    list = [];
    index = [0, 0];
    out = [];
    for part in [1 .. str.length()] {
        if (str[part] == "*")
            index = [index[1] + 1, 0];
    }
    for part in (template.explode("$"))
        list = [@list, @part.explode()];
    for part in (list) {
        if ($string.is_numeric(part)) {
            index = [index[1], index[2] + 1];
            out = [@out, toint(part)];
        } else {
            out = [@out, part];
        }
    }
    if (index[1] > index[2])
        throw(~invrelation, "Invalid relation: template one has more arguments than template two.");
    else if (index[1] < index[2])
        throw(~invrelation, "Invalid relation: template two has more arguments than template one.");
    return out;
.

public unparse_shortcut
    arg s;
    var x, line;
    
    line = "";
    for x in (s[2][2]) {
        if (type(x) == 'string)
            line = line + (line ? " " | "") + x;
        else
            line = line + (line ? " " | "") + "%" + tostr(x);
    }
    return ("\"" + s[1] + "\"").left(10) + " => \"" + line + "\"";
.

public format_commands_long
    arg cmds, type, clen;
    var def, name, c, cdef, line, o, cs, dname;
    
    o = [];
    for def in (cmds.keys()) {
        o = o + [type + " commands on " + def.name() + ":"];
        for cdef in (cmds[def]) {
            for c in (cdef[2])
                o = o + ["  " + toliteral(c[3]).left(clen) + "." + tostr(c[4]) + "()"];
        }
    }
    return o;
.

public format_commands_short
    arg cmds, type, len;
    var def, name, c, cdef, line, o, cs, dname;
    
    o = [];
    for def in (cmds.keys()) {
        o = o + [type + " commands on " + def.name() + ":"];
        for cdef in (cmds[def]) {
            for c in (cdef[2])
                o = o + ["  " + toliteral(c[3])];
        }
    }
    return o;
.



parent $libraries
object $http_lib

var $root inited 1
var $root objname 'http_lib
var $root created_on 796268969
var $root flags ['parameters, 'methods, 'code, 'core]
var $http_lib errors #[[400, ["<head><title>400 Bad Request</title></head>", "<body>", "<center><h1>400 Bad Request</h1></center>", "%s", "</body>"]], [403, ["<head><title>403 Permission Denied</title></head>", "<body>", "<center><h1>403 Permission Denied</h1></center>", "%s", "</body>"]], [404, ["<head><title>404 Not Found</title></head>", "<center><h1>404 Not Found</h1></center>", "%s", "</body>"]]]
var $http_lib gateways #[["describe", "describe?$the_pit"], ["see", "see?$the_pit"], ["who", "who"], ["display", "display?$http_lib"], ["list_method", "list_method?$http_lib.list_method"], ["project", "project"], ["help", "help?$help_summary"], ["object", "object?$http_lib"]]
var $http_lib http_methods ["GET"]
var $http_lib codes #[[200, "Ok"], [201, "Created"], [202, "Accepted"], [203, "Provisional Information"], [204, "No Content"], [300, "Multiple Choices"], [301, "Moved Permanently"], [302, "Moved Temporarily"], [303, "Method"], [304, "Not Modified"], [400, "Bad Request"], [401, "Unauthorized"], [402, "Payment Required"], [403, "Forbidden"], [404, "Not Found"], [405, "Method Not Allowed"], [406, "None Acceptable"], [407, "Proxy Authentication Required"], [408, "Request Timeout"], [409, "Conflict"], [410, "Gone"], [500, "Internal Server Error"], [501, "Not Implemented"], [502, "Bad Gateway"], [503, "Service Unavailable"], [504, "Gateway Timeout"]]
var $http_lib html_version "text/html"

public _html_traceback
    arg type, what, [more];
    var line;
    
    switch (type) {
        case 'function:
            return "function <tt>" + tostr(what) + "()</tt>";
        case 'opcode:
            return "operator <tt>" + toliteral(what) + "</tt>";
        default:
            line = $data_lib.unparse(more[2]) + "." + tostr(what) + "()";
            if (more[1] != more[2])
                line = line + " (" + $data_lib.unparse(more[1]) + ") ";
            line = line + " line " + tostr(more[3]);
            return line;
    }
.

public html_traceback
    arg status, t;
    var line, out, x;
    
    out = "<h2>" + (.filter_text([t[1][2]]))[1] + "</h2>";
    out = [out, "<i><b>Thrown by " + ._html_traceback(@t[2]) + "</b></i>", "<p>"];
    for x in [3 .. t.length()] {
        line = "<code><i>" + toliteral(t[x][1]) + "</i>: ";
        out = out + [line + ._html_traceback(@t[x]) + "</code><br>"];
    }
    return .response(status, [@out, "</p>"]);
.

public make_obj_show_href
    arg obj, [name];
    var line, oname;
    
    oname = obj.objname();
    name = [@name, "<code>" + oname + "</code>"][1];
    return "<a href=\"/bin/show?" + oname + "\">" + name + "</a>";
.

public make_method_href
    arg m;
    
    return "<a href=\"/bin/list_method?" + m[1].objname() + "." + tostr(m[2]) + "()\">" + tostr(m[2]) + "(" + m[3] + ")</a>";
.

public make_object_href
    arg obj, [args];
    var line, name, href;
    
    name = [@args, 0][1];
    if (!name)
        name = obj.namef('xref);
    href = [@args, "/bin/display?", "/bin/display?"][2];
    return "<code><a href=\"" + href + obj.objname() + "\">" + name + "</a></code>";
.

public bin_list_method
    arg [args];
    var ref, str_ref, name, obj, code, anc, out, line, x;
    
    if (!args)
        return [400, .response(400, "Must specify a method reference")];
    catch any {
        ref = $parse_lib.reference(args[1]);
        name = tosym(ref[2]);
        obj = $object_lib.to_dbref(ref[1]);
        anc = obj.find_method(name);
        code = anc.list_method(name);
        code = .filter_text(code);
        for x in [1 .. code.length()]
            line = "    " + code[x];
        str_ref = obj.objname() + "." + tostr(name) + "()";
        out = ["<head><title>" + str_ref + "</title></head>"];
        out = [@out, "<body><center><h1>" + str_ref + "</h1></center>"];
        out = [@out, "<hr><pre>", @code, "</pre>"];
    } with handler {
        switch (error()) {
            case ~methodnf:
                line = obj.objname() + "." + tostr(name) + "()";
                return [400, .response(400, line + " not found.")];
            default:
                return [400, .response(400, traceback()[1][2])];
        }
    }
    return [200, out];
.

public bin_who
    arg [args];
    var who, namel, names, times, idle, realm, x, cols, out, output, line;
    
    out = ["<head><title>Connected users to " + $motd.server_name() + "</title>", "<center><h2>Connected users to <i>" + $motd.server_name() + "</i></h2></center></head><body><pre>"];
    who = $user_db.connected();
    names = who.map('hname);
    namel = [];
    for x in (who.map('name))
        namel = [@namel, x.length()];
    cols = namel.max() + 1;
    if (cols < 5)
        cols = 5;
    times = who.map('connected_time);
    cols = [cols, times.element_maxlength() + 1];
    if (cols[2] < 7)
        cols = [cols[1], 7];
    idle = who.map('idle_time);
    cols = [@cols, idle.element_maxlength() + 1];
    if (cols[3] < 5)
        cols = cols.replace(3, 5);
    realm = who.map('realm_name, "text/html");
    out = [@out, "<hr><b>" + "Name".pad(cols[1]) + " " + "On for".pad(cols[2]) + " " + "Idle".pad(cols[3]) + " Location", "----".pad(cols[1]) + " " + "------".pad(cols[2]) + " " + "----".pad(cols[3]) + " --------</b>"];
    for x in [1 .. who.length()] {
        line = "<b>" + names[x] + "</b>" + "".pad(cols[1] - namel[x]) + " ";
        line = line + "<i>" + times[x] + "".pad(cols[2] - times[x].length()) + " ";
        line = line + idle[x] + "</i>" + "".pad(cols[3] - idle[x].length()) + " ";
        line = line + realm[x];
        out = [@out, line];
    }
    return [200, out];
.

public filter_text
    arg text;
    var x, line;
    
    // embed's characters ('>' becomes '&gt;' etc)
    for x in [1 .. text.length()] {
        if (text[x]) {
            line = text[x].replace("&", "&amp;");
            line = line.replace("<", "&lt;");
            line = line.replace(">", "&gt;");
            text = text.replace(x, line);
        }
    }
    return text;
.

public gateways
    return gateways;
.

public bin_display
    arg [args];
    var out, obj;
    
    if (!args)
        return [400, .response(400, "Must specify an object")];
    obj = (| $object_lib.to_dbref(args[1]) |);
    if (!obj)
        return [404, .response(404, "Unable to find object \"" + args[1] + "\"")];
    return [200, ["<pre>", @obj.show_in_html(), "</pre>"]];
.

public page_tail
    arg [args];
    var tail;
    
    tail = "<hr size=4><a href=\"/\"><b>" + $motd.server_name() + "</b></a>";
    if (args)
        return $buffer.from_string(tail);
    return [tail];
.

public http_methods
    return http_methods;
.

public parse_traceback
    arg traceback;
    
    return ["<head><title>Internal System Error</title></head><body><center><h1>Internal System Error</h1></center><hr><h3>Traceback:</h3><pre>"] + .filter_text($parse_lib.traceback(traceback())) + ["</pre>"] + .page_tail();
.

public list_gateways
    var out, line, gate;
    
    out = ["<ul>"];
    for gate in (gateways) {
        line = "<li><b><a href=\"/bin/" + gate[2] + "\">" + gate[1] + "</a></b>";
        out = [@out, line];
    }
    return out + ["</ul>"];
.

public make_href
    arg obj, [args];
    var line, oname, method, name;
    
    oname = obj.objname();
    name = [@args, "<code>" + oname + "</code>"][1];
    method = [@args, "/bin/show?" + oname, "/bin/show?" + oname][2];
    return "<a href=\"" + method + "\">" + name + "</a>";
.

public make_href_static
    arg obj, [args];
    var line, oname, method, name;
    
    oname = obj.objname();
    name = [@args, "<code>" + oname + "</code>"][1];
    method = [@args, "/bin/show?" + oname, "/bin/show?" + oname][2];
    return "<a href=\"" + method + "\">" + name + "</a>";
.

public process_bin_request
    arg [path];
    var gate, who;
    
    if (!path) {
        return ["text/html", .list_gateways()];
    } else {
        gate = path[1];
        path = path.subrange(2);
        if ("?" in gate) {
            path = [gate.subrange(("?" in gate) + 1), @path];
            gate = gate.subrange(1, ("?" in gate) - 1);
        }
        if (!(gate in gateways.keys()))
            return ["text/html", .get_error(400, "Unknown gateway \"" + gate + "\".")];
        return ["text/html", .(tosym("bin_" + gate))(@path)];
    }
.

public bin_describe
    arg [path];
    var obj, desc, flags;
    
    if (!path)
        return [400, .response(400, "Must specify an object.")];
    obj = (| $object_lib.to_dbref(path[1]) |) || (| $user_db.find(path[1]) |);
    if (!obj)
        return [404, .response(404, "Unable to find object \"" + path[1] + "\"")];
    
    // this is a quick hack, .description should return in html
    return [200, .build_page(obj.get_description(#[['actor, $no_one]]), obj.name())];
.

public bin_see
    arg [path];
    
    return [400, .response(400, "VRML support is pending completion, sorry!")];
.

public bin_project
    arg [path];
    var project, name;
    
    if (!path)
        return [200, $projects.list_projects()];
    name = path.to_string();
    project = (| $object_lib.to_dbref(path.to_string()) |);
    if (!project)
        return [400, .respose(400, "<hr>Unable to find project \"" + name + "\".")];
    return [200, project.generate_html()];
.

public bin_help
    arg [str];
    var node, head, tail, cout, list;
    
    if (!str)
        str = "$help_summary";
    else
        str = str[1];
    catch ~namenf {
        node = $object_lib.to_dbref(str);
    } with handler {
        return [404, .response(404, "Unable to find help node " + toliteral(str))];
    }
    head = $buffer.from_strings(["<head><title>Help: " + node.name() + "</title></head>", "<body><h2 align=center>" + node.name() + "</h2><hr>"]);
    cout = $ctext_filter.filter(node.body(), #[['formatter, $cml2_html_format], ['node, node.objname()]]);
    tail = "<hr size=4><a href=\"/\"><b>" + $motd.server_name() + "</b></a> | <a href=\"/bin/help\">Help Summary</a>";
    tail = $buffer.from_string(tail + "</body>");
    switch (type(cout)) {
        case 'string:
            cout = [cout];
        case 'frob:
            cout = `[];
    }
    return [200, head.append(cout).append(tail)];
.

public process_text
    arg what;
    var out, l, b;
    
    switch (type(what)) {
        case 'frob:
            return $ctext_filter.filter(what, #[['formatter, $cml2_html_format]]);
        case 'list:
            out = `[];
            for l in (what)
                out = out.append(.process_text(l));
            return out;
        case 'string:
            // "<br>" == `[60, 98, 114, 62]
            return `[60, 98, 114, 62].append($buffer.from_string(what));
    }
.

public response
    arg code, message;
    var name, x;
    
    if (!name = (| codes[code] |))
        return .response(500, "We had a booboo!  Invalid code: " + tostr(code));
    if (type(message) == 'string)
        message = ["<p align=center><b>" + message + "</b></p>"];
    return ["<head><title>" + tostr(code) + " " + name + "</title></head>", "<body>", "<h1 align=center>" + tostr(code) + " " + name + "</h1>", "<hr>", @message, @.page_tail()];
.

public html_version
    return html_version;
.

public build_page
    arg what, name;
    var out;
    
    return $buffer.append(`[60, 104, 101, 97, 100, 62, 60, 116, 105, 116, 108, 101, 62], $buffer.from_string(name)).append(`[60, 47, 116, 105, 116, 108, 101, 62, 60, 47, 104, 101, 97, 100, 62]).append(.process_text(what));
.

public bin_object
    arg [args];
    var out, obj;
    
    if (!args)
        return [400, .response(400, "Must specify an object")];
    obj = (| $object_lib.to_dbref(args[1]) |);
    if (!obj)
        return [404, .response(404, "Unable to find object \"" + args[1] + "\"")];
    return [200, obj.generate_html()];
.



parent $libraries
object $object_lib

var $root trusted []
var $root inited 1
var $root objname 'object_lib
var $root created_on 796268969
var $root flags ['parameters, 'methods, 'code, 'core]

public to_dbref
    arg obj;
    var type, dbref;
    
    type = type(obj);
    switch (type) {
        case 'string:
            if (obj[1] == "$") {
                dbref = (| get_dbref(tosym(obj.subrange(2))) |);
            } else if (obj[1] == "#") {
                obj = obj.subrange(2);
                if ($string.is_numeric(obj))
                    dbref = (| todbref(toint(obj)) |);
                else
                    throw(~objnf, "Cannot find object \"#" + obj + "\".");
            }
            if (!dbref)
                dbref = (> get_dbref(tosym(obj.replace(" ", "_").lowercase())) <);
            return dbref;
        case 'dbref:
            return obj;
        default:
            return (> get_dbref(obj) <);
    }
.

public get_name
    arg obj, [args];
    var meth;
    
    // get_name(obj, 'method, [args]) (3rd arg must be a list)
    if (!valid(obj))
        return tostr(obj);
    meth = [@args, 'name][1];
    args = [@args, [], []][2];
    return obj.(meth)(@args);
.

public see_perms
    arg obj, [args];
    var str, r, who, encapsulate;
    
    encapsulate = [@args, ["[", "]"]][1];
    str = encapsulate[1];
    if (obj.has_flag('core))
        str = str + "*";
    else
        str = str + "-";
    if (obj.has_flag('fertile))
        str = str + "f";
    else
        str = str + "-";
    if (obj.has_flag('methods, sender()))
        str = str + "m";
    else
        str = str + "-";
    if (obj.has_flag('parameters, sender()))
        str = str + "p";
    else
        str = str + "-";
    if (obj.has_flag('code, sender()))
        str = str + "c";
    else
        str = str + "-";
    return str + encapsulate[2];
.

public str_to_objlist
    arg args;
    var out, x, obj;
    
    if ("," in args)
        args = args.explode_english_list();
    else
        args = args.explode();
    return .list_to_objlist(args);
.

public list_to_objlist
    arg args;
    var out, x, obj;
    
    out = #[['valid, []], ['invalid, []]];
    for x in (args) {
        obj = (| .to_dbref(x) |);
        if (obj)
            out = out.add_elem('valid, obj);
        else
            out = out.add_elem('invalid, x);
    }
    return out;
.

public parse_method_flags
    arg flags;
    
    return " " + ('disallow_overrides in flags ? "d" | "-") + ('syncronized in flags ? "s" | "-") + ('locked in flags ? "l" | "-") + ('native in flags ? "n" | "-");
.



parent $libraries
object $integer

var $root inited 1
var $root objname 'integer
var $root created_on 796268969
var $root flags ['parameters, 'methods, 'code, 'core]

public n_to_nth
    arg number;
    var tens_digit_is_1, ones_digit, single_digit;
    
    if (type(number) != 'integer)
        throw(~type, "Must receive an integer");
    ones_digit = abs(number) % 10;
    tens_digit_is_1 = abs(number) / 10 % 10 == 1;
    single_digit = abs(number) < 10;
    if (ones_digit in [1, 2, 3] && !tens_digit_is_1) {
        switch (ones_digit) {
            case 1:
                return tostr(number) + "st";
            case 2:
                return tostr(number) + "nd";
            case 3:
                return tostr(number) + "rd";
        }
    } else {
        return tostr(number) + "th";
    }
.

public parse_range
    arg range_str;
    var r1, r2;
    
    // ("1-5") => (1, 5)      -- 1, 5
    // ("1-$") => (1, 'end)   -- 1, 'end (end number)
    // (".-3") => ('cur, 3)   -- 'cur (current number), 3
    // ("#-3") => ('bgn, 3)   -- 'bgn (beginning number), 3
    range_str = $parse_lib.reference(range_str, "-");
    r1 = range_str[1];
    r2 = range_str[2];
    
    //
    if (!r2)
        r2 = r1;
    if (!toint(r1)) {
        if (r1 == ".")
            r1 = 'cur;
        else if (r1 == "#")
            r1 = 'bgn;
        else
            throw(~type, "Beginning range invalid.");
    } else {
        r1 = toint(r1);
    }
    
    //
    if (!toint(r2)) {
        if (r2 == "$")
            r2 = 'end;
        else
            throw(~type, "Ending range invalid.");
    } else {
        r2 = toint(r2);
    }
    return [r1, r2];
.

public to_english
    arg num;
    var num_str, sign;
    
    // 12500 => "12,500"
    // if (abs(num) < 9999)
    //  return tostr(num);
    sign = num ? abs(num) / num | 1;
    num = abs(num);
    num_str = "";
    while (num > 999) {
        num_str = "," + tostr(1000 + num % 1000).subrange(2) + num_str;
        num = num / 1000;
    }
    num_str = tostr(num) + num_str;
    return (sign == 1 ? "" | "-") + num_str;
.

public range
    arg x, y;
    var list, element;
    
    // creates a list of every number between x and y 
    list = [];
    for element in [x .. y]
        list = list + [element];
    return list;
.



parent $libraries
object $parse_lib

var $root inited 1
var $root objname 'parse_lib
var $root created_on 796268969
var $root flags ['parameters, 'methods, 'code, 'core]
var $parse_lib boolean_strs [["yes", "true", "1", "on"], ["no", "false", "0", "off"]]

public reference
    arg string, [sep];
    var middle, ref;
    
    // receives: "<object><seperator><method/param>"
    // returns ["<object>", "<method/param>"]
    // seperator defaults to a period.
    sep = [@sep, "."][1];
    middle = sep in string;
    if (!middle)
        ref = [string, ""];
    else if (middle == 1)
        ref = [sender().namef(['objname]), string.subrange(2)];
    else
        ref = [string.subrange(1, middle - 1), string.subrange(middle + 1)];
    
    // assumes "()" will appear at the end of the reference if at all,
    // and strips it off if present
    if ("()" in ref[2])
        ref = [ref[1], ref[2].subrange(1, ("()" in ref[2]) - 1)];
    return ref;
.

public object_match
    arg name, [who];
    var msg;
    
    // .object_match("name"[, who])
    // -> 0        name was the empty string
    // -> ~objnf   nothing matched name
    // -> ~ambig   more than one object matched name
    // Attempt to match an object name with who.match_environment().  If one is found, return it.  Else, print a message and return one of the above false values.
    // 'who' defaults to sender().
    who = who ? who[1] | sender();
    if (!name) {
        (| who.tell("You must give the name of something.") |);
        return 0;
    }
    catch ~objnf, ~ambig {
        return who.match_environment(name);
    } with handler {
        switch (error()) {
            case ~objnf:
                msg = "I don't see any \"" + name + "\" here.";
            case ~ambig:
                msg = "I don't know which \"" + name + "\" you mean.";
        }
        (| who.tell(msg) |);
        return error();
    }
.

public tell_error
    arg problem, [args];
    var who, syntax, line, sprefix, prefix;
    
    // arg 1 == error
    // arg 2 (opt) == syntax
    // arg 3 (opt) == who
    // arg 4 (opt) == subbing object (in place of 'Object') -- string.
    syntax = [@args, 0][1];
    who = [@args, sender(), sender()][2];
    sprefix = (| sender().setting("error-syntax-prefix") |) || "=> ";
    prefix = (| sender().setting("error-prefix") |) || "!  ";
    if (syntax)
        who.tell(sprefix + "Syntax: `" + syntax + "`");
    if (problem) {
        if (type(problem) == 'string) {
            problem = $string.wrap_line(problem, (| who.linelen() |) || 79, prefix, 1);
        } else {
            for line in [1 .. problem.length()]
                problem = problem.replace(line, prefix + problem[line]);
        }
        who.tell(problem);
    }
    throw(~stop, "", 'no_traceback);
.

public usage
    arg method, [objname];
    var code, extracted;
    
    // .usage(method[, objname])
    // Extract initial comments from the given method, returning them as a list of strings.
    // Throw ~methodnf if objname does not define method.
    // objname defaults to sender.
    objname = objname ? objname[1] | sender();
    objname = objname.find_method(method);
    code = objname.list_method(method);
    extracted = [];
    if (code[1] == "disallow_overrides;")
        code = code.delete(1);
    if (("arg " in code[1]) == 1)
        code = code.delete(1);
    if (("var " in code[1]) == 1)
        code = code.delete(1);
    while (!code[1])
        code = code.delete(1);
    while (("//" in code[1]) == 1) {
        extracted = [@extracted, code[1].replace("//", "")];
        code = code.delete(1);
    }
    return extracted;
.

public match
    arg string;
    var loc, obj;
    
    // called by $user.match_* methods, simply parses up the basic me/here/$*
    if (string == "me")
        return sender();
    if (string == "here")
        return sender().location();
    if (string && string[1] == "$") {
        obj = todbref(string.subrange(2));
        if (!valid(obj))
            throw(~objnf, "No such object " + string);
        return obj;
    } else {
        return 0;
    }
.

public boolean
    arg str;
    
    if (str in boolean_strs[1])
        return 1;
    else if (str in boolean_strs[2])
        return 0;
    else
        throw(~unknown, "Boolean flag not recognized.");
.

public full_reference
    arg str, [args];
    var sep, defobj, middle, ref, type, match;
    
    defobj = [@args, sender()][1];
    match = [@args, [$object_lib, 'to_dbref], [$object_lib, 'to_dbref]][2];
    if ("()" in str)
        str = str.subrange(1, ("()" in str) - 1);
    if ("." in str) {
        type = 'method;
        sep = ".";
    } else if ("," in str) {
        type = 'parameter;
        sep = ",";
    } else {
        type = 'unknown;
        sep = ".";
    }
    middle = sep in str;
    if (!middle)
        ref = [(> match[1].(match[2])(str) <), ""];
    else if (middle == 1)
        ref = [defobj, str.subrange(2)];
    else
        ref = [(> match[1].(match[2])(str.subrange(1, middle - 1)) <), str.subrange(middle + 1)];
    return [type, ref[1], ref[2] ? tosym(ref[2]) | 0];
.

public traceback
    arg traceback, [args];
    var line, out, pre, lines, cur, x, error;
    
    // $parse_lib.traceback(traceback(), lines, pre);
    // -1 lines represents the full error
    // pre is set to "! " unless otherwise specified.
    lines = [@args, -1][1];
    pre = [@args, "! ", "! "][2];
    error = [@args, 0, 0, 0][3];
    
    //
    out = [pre + "=> " + traceback[1][2]];
    pre = pre + "   ";
    
    //
    if (error == 0)
        out = [@out, pre + "Thrown by " + ._traceback(@traceback[2].subrange(2))];
    else
        out = [@out, pre + "Error " + toliteral(error) + " caused by " + ._traceback(@traceback[2].subrange(2))];
    
    //
    for x in [1 .. traceback.length() - 2] {
        if (x <= lines || lines == -1) {
            line = $data_lib.unparse(traceback[x + 2][1]) + ": ";
            line = line + ._traceback(@traceback[x + 2].subrange(2));
            out = [@out, pre + line];
        }
    }
    return out;
.

public _traceback
    arg what, [more];
    var line;
    
    if (more) {
        if (more[1] == more[2])
            return $data_lib.unparse(more[1]) + "." + tostr(what) + "() line " + tostr(more[3]);
        else
            return $data_lib.unparse(more[2]) + "." + tostr(what) + "() (" + $data_lib.unparse(more[1]) + ") line " + tostr(more[3]);
    } else {
        return tostr(what);
    }
.

public options
    arg line, [defaults];
    var loc, opt, x, out, defs;
    
    // this will not pay attention to groupings with quotes, should add
    // the functionality in later.
    // templates: #[["opt", [0/1, 0/1]]]; / #[['opt, [bool, value]]]; 
    defaults = [@defaults, #[]][1];
    if (!("-" in line || "+" in line))
        return [line.explode(), defaults];
    line = line.explode();
    out = line;
    for x in [1 .. line.length()] {
        if (line[x][1] in ["-", "+"]) {
            out = out.setremove(line[x]);
            opt = line[x].subrange(2);
            defs = (| defaults[opt] |) || [0, ""];
            defs = [line[x][1] in ["+"], defs[2]];
            if (defs[2]) {
                if (line.length() >= x + 1) {
                    defs = [defs[1], line[x + 1]];
                    out = out.setremove(line[x + 1]);
                }
            }
            defaults = defaults.add(opt, defs);
        }
    }
    return [out, defaults];
.

public my_options
    arg line, options;
    var args, word, c, pos, key, p, p2;
    
    word = "";
    args = "";
    while (line) {
        c = line[1];
        switch (c) {
            case "+":
                pos = " " in line;
                if (!pos) {
                    key = line.subrange(2);
                    line = "";
                } else {
                    key = line.subrange(2, pos);
                    line = line.subrange(pos);
                }
                key = tosym(key);
                catch ~keynf {
                    if (options[key][1] == 'bool)
                        options = options.add(key, ['bool, 1]);
                    else
                        throw(~opttype, "Option " + tostr(key) + " is not boolean.");
                } with handler {
                    throw(~badopt, "Option " + tostr(key) + " is not a valid option.");
                }
            case "-":
                pos = " " in line;
                if (!pos) {
                    key = line.subrange(2);
                    line = "";
                } else {
                    key = line.subrange(2, pos);
                    line = line.subrange(pos);
                }
                key = tosym(key);
                catch ~keynf {
                    if (options[key][1] == 'bool)
                        options = options.add(key, ['bool, 0]);
                    else
                        throw(~opttype, "Option " + tostr(key) + " is not boolean.");
                } with handler {
                    throw(~badopt, "Option " + tostr(key) + " is not a valid option.");
                }
            case "=":
                key = word;
                word = "";
                pos = " " in line;
                if (!pos)
                    pos = line.length();
                key = tosym(key);
                catch ~keynf {
                    if (options[key][1] == 'value)
                        options = options.add(key, ['value, line.subrange(2, pos - 1)]);
                    else
                        throw(~opttype, "Option " + tostr(key) + " is not a value.");
                } with handler {
                    throw(~badopt, "Option " + tostr(key) + " is not a valid option.");
                }
                line = line.subrange(pos + 1);
            case " ":
                args = args + word;
                word = "";
                line = line.subrange(2);
            default:
                pos = " " in line;
                if (!pos)
                    pos = line.length();
                p = "=" in line;
                if (p > 0 && p < pos) {
                    word = word + line.subrange(1, p - 1);
                    line = line.subrange(p);
                } else {
                    p = "-" in line;
                    p2 = "+" in line;
                    if (p2 < p)
                        p = p2;
                    if (p > 0 && p < pos) {
                        args = args + line.subrange(1, p - 1);
                        line = line.subrange(p);
                    } else {
                        args = args + line.subrange(1, pos);
                        line = line.subrange(pos + 1);
                    }
                }
        }
    }
    args = args + word;
    for key in (options.keys()) {
        word = options[key][2];
        if (type(word) == 'string)
            word = $string.trim(word);
        options = options.keys(key, word);
    }
    return [args, options];
.

public xreference
    arg str, [args];
    var out, def, obj, reg, tmp, sep, member, p, obj, match;
    
    obj = [@args, sender()][1];
    match = [@args, [sender(), 'match_environment, []], [sender(), 'match_environment, []]][2];
    reg = $string.match_regexp("<.*>", str);
    if (reg) {
        def = str.subrange(reg[1][1] + 1, reg[1][2] - 2);
        tmp = str.subrange(1, reg[1][1] - 1);
        str = tmp + str.subrange(reg[1][1] + reg[1][2]);
    }
    if ("(" in str)
        str = str.subrange(1, ("(" in str) - 1);
    if ("." in str) {
        out = ['method];
        sep = ".";
    } else if ("," in str) {
        out = ['variable];
        sep = ",";
    } else {
        out = ['object];
    }
    if (sep) {
        p = sep in str;
        member = tosym(str.subrange(p + 1));
        if (p > 1)
            obj = str.subrange(1, p - 1);
    } else {
        obj = str;
    }
    if (type(obj) != 'dbref)
        obj = (> match[1].(match[2])(obj, @match[3]) <);
    if (def && type(def) != 'dbref)
        def = (> match[1].(match[2])(def, @match[3]) <);
    else
        def = obj;
    return out + [obj, def, member];
.

public range
    arg str;
    var out;
    
    out = str.explode("-");
    if (out.length() == 1) {
        out = [(> ._range(str) <), 'single];
    } else if (out.length() == 2) {
        out = out.replace(1, (> ._range(out[1]) <));
        out = out.replace(2, (> ._range(out[2]) <));
    } else {
        throw(~range, "Invalid range reference.");
    }
    return out;
.

public _range
    arg str;
    
    if (str.is_numeric()) {
        return toint(str);
    } else {
        switch (str[1]) {
            case "$":
                return 'end;
            case ".":
                return 'current;
            case "^":
                return 'start;
            default:
                throw(~range, "Invalid range reference.");
        }
    }
.

public getopt
    arg line, [defaults];
    var out, newlist, part, v, opt, t, templates, keys, key, l, x;
    
    // submit: [["template", value], [...]];
    // => if value is 1, it will take the next part of the string
    // receive: [["template", "flag", bool, value]], [...]]; 
    line = line.explode_quoted();
    out = [];
    newlist = [];
    defaults = (| defaults[1] |) || [];
    templates = defaults.slice(1);
    x = 1;
    l = line.length();
    while (1) {
        if (x > l)
            break;
        if (line[x][1] in ["-", "+"]) {
            opt = 0;
            v = "";
            part = line[x].subrange(2);
            for t in [1 .. templates.length()] {
                if ("=" in part) {
                    part = part.explode("=");
                    v = (| part[2] |) || "";
                    part = part[1];
                }
                if ($string.match_template(templates[t], part)) {
                    opt = [templates[t], part, line[x][1] == "+"];
                    if ((| defaults[t][2] |) && !v) {
                        if (x + 1 <= l) {
                            x = x + 1;
                            if (line[x] == "=") {
                                if (x + 1 <= l)
                                    x = x + 1;
                            }
                            v = line[x];
                        }
                    }
                    opt = opt + [v];
                }
            }
            if (!opt)
                opt = [0, part, line[x][1] == "+", ""];
            out = out + [opt];
        } else {
            newlist = newlist + [line[x]];
        }
        x = x + 1;
    }
    return [newlist, out];
.

public buildref
    arg type, obj, def, name;
    var line;
    
    line = obj.objname();
    if (obj != def)
        line = line + "<" + def.objname() + ">";
    if (type == 'object)
        return line;
    if (type == 'method)
        return line + "." + tostr(name) + "()";
    if (type == 'parameter)
        return line + "," + tostr(name);
.



parent $libraries
object $code_lib

var $root inited 1
var $root objname 'code_lib
var $root created_on 796268969
var $root flags ['parameters, 'methods, 'code, 'core]
var $code_lib quotes [["\"Abandon all hope, ye who enter here.\""], ["\"Pull out Wedge, your not doing any more good back there.\"", "", "- Luke Skywalker, StarWars"], ["\"God is in the details.\""], ["\"I don't practice what I preach,", "because I'm not the type of person I'm preaching too.\"", "", "- Bob Dobbs"], ["\"I haven't shaved for six years.", "I seem to be cursed with a thin beard.\"", "", "- Calvin, Age 6 (of Calvin & Hobbes)"], ["\"I may be a Dreamer, but I'm not the only one.\"", "", "- John Lennon"], ["\"C code.  C code run.  Run, Code, Run!  Please!?\"", "", "- Anonymous C hacker."], ["\"They who dream by day are cognizant of many things", "which escape those who dream only by night.\"", "", "- Edgar Allan Poe"], ["\"The good die first,", "and they whose hearts are dry as a summer dust", "burn to the socket.\"", "", "- William Wordsworth"], ["\"Banning guns to prevent murder", "is like banning word processors to prevent libel.\"", "", "- Unknown"], ["\"You will not be punished for your anger,", "you will be punished by your anger.\"", "", "- Buddha"], ["What part of:", "", "main() { printf(&unix[\"\\021%six\\012\\0\"],(unix)[\"have\"]+\"fun\"-0x60);}", "", "do you not understand?", "", "(taken from the 1987 Obfuscated C Code Contest)"], ["\"The goal of computer science is to build something that will", "last at least until we've finished building it.\""], ["\"Give me ambiguity or give me something else.\""], ["\"We are born naked, wet and hungry. Then things get worse.\""], ["\"Make it idiot proof and someone will make a better idiot.\""], ["\"Lottery: A tax on people who are bad at math.\""], ["\"There's too much blood in my caffeine system.\""], ["\"Artificial Intelligence usually beats real stupidity.\""], ["\"Ever notice how fast MS-Windows runs? Neither did I.\""], ["\"Very funny, Scotty. Now beam down my clothes.\""], ["\"Consciousness: that annoying time between naps.\""], ["\"The gene pool could use a little chlorine.\""], ["\"When there's a will, I want to be in it.\""], ["\"Change is inevitable, except from a vending machine.\""], ["\"MS-Windows is a virus:", "it takes over your computer and makes it run bad.\""], ["\"I have not failed 10,000 times,", "I have sucessfully found 10,000 ways that do not work.\"", "- Thomas Edison"], ["\"The difference between literature and journalism is", "that journalism is unreadable and literature is not read.\"", "", "- Oscar Wilde (1854-1900)"], ["\"The man who reads nothing at all is better educated", "than the man who reads nothing but newspapers.\"", "", "- Thomas Jefferson (1743-1826)"], ["\"In the mind of the beginner, there are many possibilities.", "In the mind of the expert there are few.\"", "", "- Shunryu Suzuki"], ["\"Time is a great teacher, but unfortunately it kills all of its pupils.\"", "", "- Hector Berlioz"], ["\"After I'm dead I'd rather have people ask", "why I have no monument, than why I have one.\"", "", "- Cato the Elder (234-249 B.C.)"], ["\"I loathe people who keep dogs. They are cowards who haven't", "got the guts to bite people themselves.\"", "", "-August Strindberg"], ["\"By the time I'd grown up, I natrually supposed that I'd be grown up.\"", "", "-Eve Babitz"], ["\"Every place has a spirit:", "it may be good, it may be bad", "it can be restful as eternity.", "This place-spirit can inhabit a book, a house, a town, a valley;", "the nature of the spirits is they remain.\"", "", "- William S. Burroughs"]]
var $code_lib command_argument_types [['any, "any"], ['text, "text"], ['object, "object"], ['thing, "thing"], ['user, "user"], ['descendant, "descendant of *"], ['this, "this"], ['number, "number"]]

public set_obj_param
    arg obj, param, val;
    var set_code;
    
    // set param on obj to val
    if (!(sender() == $root || $sys.is_admin(sender())))
        throw(~perm, "Sender not admin or $root");
    if (!valid(obj))
        throw(~objnf, "Object " + $data_lib.unparse(obj) + " is not a valid object.");
    if (!(param in obj.parameters()))
        throw(~parmnf, "Parameter " + $data_lib.unparse(param) + " is not a parmater on " + $data_lib.unparse(obj) + ".");
    if ('__set_any in $obj.methods())
        throw(~methexist, "Method '__set_any already defined on target object.");
    
    // Copy _set_any to obj
    set_code = .list_method('_set_var);
    obj.compile(set_code, '__set_var);
    
    // Set and delete method
    catch any {
        obj.__set_var(param, val);
        obj.del_method('__set_var);
    } with handler {
        obj.del_method('__set_var);
        rethrow(error());
    }
.

public _set_var
    arg param, val;
    
    if (sender() != $code_lib)
        throw(~perm, "Sender not $code_lib");
    return set_var(param, val);
.

public quotes
    return quotes;
.

public add_random_quote
    arg quote, [from];
    
    if (!$sys.is_admin(sender()))
        throw(~perm, "Sender is not an owner");
    if (type(quote) != 'string)
        throw(~type, "Quote must be given as a string.");
    quote = "\"" + quote + "\"";
    quote = $string.wrap_line(quote, 70);
    quote = from ? [@quote, "     - " + from[1]] | quote;
    quotes = quotes + [quote];
.

public generate_listing
    arg who, [args];
    var meths, methval, input, output, element, header, title, cols, x, z, len, line, linea, y;
    
    // called by one of the who_cmds, does all the grunge work.
    title = [@args, "Connected Users"][1];
    meths = [@args, [['name], ['connected_time], ['idle_time], ['realm_name]], [['name], ['connected_time], ['idle_time], ['realm_name]]][2];
    header = [@args, ["Name", "On for", "Idle", "Location"], ["Name", "On for", "Idle", "Location"], ["Name", "On for", "Idle", "Location"]][3];
    cols = [@args, [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]][4];
    
    // get values using $list.map
    element = $list.map(who, meths[1][1], @meths[1].subrange(2));
    input = [];
    for x in (element) {
        input = [@input, [x]];
        if (x.length() > cols[1])
            cols = cols.replace(1, x.length());
    }
    
    // ok, get on with it.
    for x in [2 .. meths.length()] {
        methval = $list.map(who, meths[x][1], @meths[x].subrange(2));
        for z in [1 .. methval.length()] {
            if (methval[z].length() > cols[x])
                cols = cols.replace(x, methval[z].length());
            input = input.replace(z, [@input[z], methval[z]]);
        }
    }
    
    // this will remove columns with no information in them.
    z = [];
    for x in [1 .. cols.length()] {
        if (!cols[x])
            z = [@z, x];
    }
    for x in (z) {
        cols = cols.delete(x);
        meths = meths.delete(x);
        header = header.delete(x);
        for y in [1 .. input.length()]
            input = input.replace(y, input[y].delete(x));
    }
    
    // the header.
    len = (| sender().linelen() |) || 79;
    line = "";
    linea = "";
    for x in [1 .. cols.length()] {
        line = line + header[x].pad(cols[x] + 2);
        linea = linea + "".pad(header[x].length(), "-").pad(cols[x] + 2);
    }
    output = ["--- " + title + " (" + tostr(input.length()) + ") ---"];
    output = [@output, line.pad(len)];
    output = [@output, linea.pad(len)];
    
    // tell the rest:
    for x in (input) {
        line = "";
        for z in [1 .. cols.length()]
            line = line + x[z].pad(cols[z] + 2);
        output = [@output, line.pad(len).trim()];
    }
    return [@output];
.

public random_quote
    var which;
    
    which = random(quotes.length());
    if (which)
        return quotes[which];
    return [];
.

public valid_email
    arg email;
    var host, user, ip, tmp;
    
    email = email.explode("@");
    if (email.length() != 2)
        return ['invalid, email, ""];
    
    // if we want we can do something overly extensive with this, but we will not
    user = email[1];
    host = email[2];
    return ['valid, user, host];
    if (toint(host[1])) {
        tmp = hostname(host);
        if (tmp == "-1")
            return ['invip, user, host];
    } else {
        tmp = $sys.ip(host);
        if (tmp == "-1")
            return ['invhostname, user, host];
    }
    return ['valid, user, host];
.

public unparse_command
    arg command;
    var x, line;
    
    // command should be passed as a list, and can either be a command
    // or shortcut.  This will return a string.
    if (command.length() == 2)
        return toliteral(command[1]);
    line = "";
    for x in (command[3]) {
        if (type(x) == 'string)
            line = line + (line ? " " | "") + x;
        else
            line = line + (line ? " " | "") + "%" + tostr(x);
    }
    return "\"" + command[1] + "\" => \"" + line + "\"";
.

public parse_name
    arg name;
    var article, args, flag;
    
    // used to parse $named names and aliases.
    args = $parse_lib.getopt(name, [["u?nique"], ["p?roper"], ["n?ormal"]]);
    flag = args[2];
    name = args[1].to_string();
    flag = (| flag[flag.length()] |) || ["n?ormal", "n", 1, ""];
    switch (flag[1]) {
        case "p?roper":
            article = 'prop;
        case "u?nique":
            article = 'uniq;
        default:
            // n?ormal
            article = 'normal;
    }
    name = name.explode(",");
    return [[name[1], article], name.subrange(2)];
.

public random_word
    arg [args];
    var x, out, min, max, con, vow, flag, lcon, lvow, extra;
    
    min = [@args, 5][1];
    max = [@args, 9, 9][2];
    extra = [@args, "", "", ""][3];
    x = random(max - min) + min;
    out = "";
    con = "bcdfghjklmnpqrstvwxz" + extra;
    vow = "aeiouy";
    lcon = con.length();
    lvow = vow.length();
    while (out.length() < x) {
        if (!flag)
            out = out + con[random(lcon)];
        else
            out = out + vow[random(lvow)];
        flag = !flag;
    }
    return out;
.

public punctuation_type
    arg str;
    var end;
    
    end = str.length();
    switch (str[end]) {
        case "!":
            return "exclaim";
        case "?":
            return "ask";
        case ".":
            return "say";
        case ")":
            if (end > 1) {
                switch (str[end - 1]) {
                    case ";":
                        return "wink";
                    case ":":
                        return "smile";
                    case "8":
                        return "grin";
                    default:
                        return "say";
                }
            }
        case "(":
            if (end > 1 && str[end - 1] in [":", "8"])
                return "frown";
    }
    return "say";
.

public check_encrypted
    arg cstr, str;
    
    return str.crypt(cstr.subrange(1, 2)) == cstr;
.

public user_type
    arg user;
    var p;
    
    // The reason for this is because the parents list may get messed up
    // if they add/remove parents.
    p = user.parents();
    if ($user in p)
        return 'user;
    else if ($builder in p)
        return 'builder;
    else if ($programmer in p)
        return 'programmer;
    else if ($admin in p)
        return 'admin;
    else
        return 'none;
.

public command_argument_types
    return command_argument_types;
.

public get_command_argument_type
    arg type;
    var x, m;
    
    for x in [1 .. command_argument_types.length()] {
        m = $string.match_pattern(command_argument_types[x][2], type);
        if (type(m) == 'list)
            return [command_argument_types[x][1], m];
    }
    throw(~command, "Invalid command argument type \"" + type + "\"");
.

public verify_code
    arg code, method;
    var line, m, warns;
    
    warns = [];
    method = "\\." + tostr(method) + "\\(";
    for line in [1 .. code.length()] {
        m = $string.match_regexp(method, code[line]);
        if (m) {
            warns = warns + ["Warning: Possible Recursion, line " + tostr(line) + ":"];
            warns = warns + ["  " + code[line]];
            warns = warns + ["".pad(m[1][1] + 2, "-") + "".pad(m[1][2] - 2, "^")];
        }
    }
    return warns;
.

public default_description_flags
    return #[['prose, 1], ['contents, 1], ['extra, 1], ['visibility, 1], ['actor, sender()], ['exclude, []], ['brief, 0]];
.

public generate_object_listing
    arg objs, multi, [args];
    var line, obj, col, name, fmt, out;
    
    if (!objs) {
        out = ["** None **"];
    } else {
        col = ((| sender().linelen() |) || 79) / 10;
        fmt = "%3L%" + tostr(col * 4) + "L %" + tostr(col) + "L %" + tostr(col) + "R ";
        out = [fmt.format("#", "Name", "Perms", "Size") + "Manager"];
        col = col * 4;
        for obj in (objs) {
            line = fmt.format(obj.(multi)(@args).length(), obj.namef('xref), $object_lib.see_perms(obj, ["", ""]), obj.size());
            name = obj.manager().namef('xref);
            if (name.length() > col)
                name = name.pad(col);
            out = out + [line + name];
        }
    }
    return out;
.



parent $libraries
object $list

var $root inited 1
var $root objname 'list
var $root created_on 796268969
var $root flags ['parameters, 'methods, 'code, 'core]

public to_string
    arg list, [sep];
    var str, part;
    
    // uses $data_lib.unparse() rather than tostr()
    if (!list)
        return "";
    sep = [@sep, " "][1];
    str = tostr(list[1]);
    for part in (list.delete(1))
        str = str + sep + tostr(part);
    return str;
.

public to_english
    arg list, [options];
    var empty, and, sep;
    
    // uses $data_lib.unparse() rather than tostr()
    empty = [@options, "nothing"][1];
    switch (list.length()) {
        case 0:
            return empty;
        case 1:
            return tostr(list[1]);
    }
    and = [@options, " and ", " and "][2];
    sep = [@options, ", ", ", ", ", "][3];
    return .to_string(list.delete(list.length()), sep) + and + tostr(list[list.length()]);
.

public map
    arg list, method, [args];
    var out, x;
    
    // call 'method on each object, return results.
    out = [];
    for x in (list)
        out = [@out, x.(method)(@args)];
    return out;
.

public filter
    arg list, method, [args];
    var out, x;
    
    // similar to .map, but returns a list of objects which returned a
    // true value from 'method.
    out = [];
    for x in (list) {
        if (x.(method)(@args))
            out = [@out, x];
    }
    return out;
.

public sort
    arg list, [sortby];
    
    // calls ._sort().  Does not set an element to sort by yet, but should
    // eventually will have to fix.
    return ._sort(list, 1, list.length());
.

public _sort
    arg lst, x, y;
    var p, i, j;
    
    switch (y - x + 1) {
        case 0, 1:
            return lst;
        case 2:
            if (lst[x] <= lst[y])
                return lst;
            p = lst[x];
            lst = lst.replace(x, lst[y]);
            lst = lst.replace(y, p);
            return lst;
        case 3:
            if (lst[x] <= lst[x + 1]) {
                if (lst[x + 1] <= lst[y]) {
                } else if (lst[x] <= lst[y]) {
                    p = lst[x + 1];
                    lst = lst.replace(x + 1, lst[y]);
                    lst = lst.replace(y, p);
                } else {
                    p = lst[x];
                    lst = lst.replace(x, lst[y]);
                    lst = lst.replace(y, lst[x + 1]);
                    lst = lst.replace(x + 1, p);
                }
            } else if (lst[x] <= lst[y]) {
                p = lst[x];
                lst = lst.replace(x, lst[x + 1]);
                lst = lst.replace(x + 1, p);
            } else if (lst[x + 1] <= lst[y]) {
                p = lst[x];
                lst = lst.replace(x, lst[x + 1]);
                lst = lst.replace(x + 1, lst[y]);
                lst = lst.replace(y, p);
            } else {
                p = lst[x];
                lst = lst.replace(x, lst[y]);
                lst = lst.replace(y, p);
            }
            return lst;
    }
    p = lst[x];
    i = x;
    j = y;
    while (1) {
        while (i < j && p <= lst[j])
            j = j - 1;
        if (i == j)
            break;
        lst = lst.replace(i, lst[j]);
        i = i + 1;
        while (i < j && p >= lst[i])
            i = i + 1;
        if (i == j)
            break;
        lst = lst.replace(j, lst[i]);
        j = j - 1;
    }
    lst = lst.replace(i, p);
    lst = ._sort(lst, x, i - 1);
    lst = ._sort(lst, i + 1, y);
    return lst;
.

public columnize
    arg list, cols, [rest];
    var width, lines, line, separator, linelength, curcol;
    
    // turn [...] into ".   .   ."
    // rest[1]==separator; rest[2]==linelength
    separator = [@rest, "   "][1];
    linelength = [@rest, 78, 78][2];
    width = linelength / cols - separator.length();
    lines = [];
    while (list) {
        line = list[1].pad(width);
        list = list.subrange(2);
        for curcol in [2 .. cols] {
            if (list) {
                line = line + separator + list[1].pad(width);
                list = list.subrange(2);
            }
        }
        lines = [@lines, line];
    }
    return lines;
.

public reverse
    arg list;
    var elm, reversed;
    
    // .reverse(list)
    // -> list with its elements reversed
    reversed = [];
    for elm in (list)
        reversed = [elm, @reversed];
    return reversed;
.

public compress
    arg list;
    var out, last, x;
    
    // [a,a,b,b,c,c,d,d] => [a,b,c,d]
    // removes duplicate entries in a list
    out = [];
    for x in (list) {
        if (!(x in out))
            out = [@out, x];
    }
    return out;
.

public last
    arg list;
    
    return list[list.length()];
.

public count
    arg elem, list;
    var count;
    
    // count of elem in list
    while (elem in list) {
        count = count + 1;
        list = list.subrange((elem in list) + 1);
    }
    return count;
.

public element_maxlength
    arg list;
    var elm, max, len;
    
    for elm in (list) {
        len = tostr(elm).length();
        if (len > max)
            max = len;
    }
    return max;
.

public nth_element_maxlength
    arg lists, element;
    var list, max, len;
    
    // Returns longest string whose index is element in one of the lists in lists.
    if (type(element) != 'integer)
        throw(~type, "First argument is not an integer");
    if (type(lists) != 'list)
        throw(~type, "Second argument is not a list");
    max = 0;
    for list in (lists) {
        len = tostr(list[element]).length();
        if (len > max)
            max = len;
    }
    return max;
.

public numbered_text
    arg text;
    var line;
    
    // receives a list of strings, returns that list with line numbers pre-pended
    for line in [1 .. text.length()]
        text = text.replace(line, (line < 10 ? " " | "") + tostr(line) + ": " + text[line]);
    return text;
.

public slice
    arg big_list, element;
    var list, ret_list;
    
    // Return elementh' element of all lists in big_list
    // No type or length checking done for speed purposes.
    ret_list = [];
    for list in (big_list)
        ret_list = [@ret_list, list[element]];
    return ret_list;
.

public swap
    arg list, a, b;
    var holder;
    
    // swap elements at indexes a and b
    holder = (> list[a] <);
    list = (> list.replace(a, list[b]) <);
    list = list.replace(b, holder);
    return list;
.

public max
    arg list;
    
    // return greatest element of list (no type checking performed)
    // if list is [], returns 0
    while (list.length() > 1) {
        if (list[1] > list[2])
            list = list.delete(2);
        else
            list = list.delete(1);
    }
    return [@list, 0][1];
.

public min
    arg list;
    
    // return least element of list (no type checking performed)
    // if list is [], returns 0
    while (list.length() > 1) {
        if (list[1] < list[2])
            list = list.delete(2);
        else
            list = list.delete(1);
    }
    return [@list, 0][1];
.

public lcolumnize
    arg list, [args];
    var line, part, lines, max, cols, col, width, len, sep;
    
    len = [@args, (| sender().linelen() |) || 79][1];
    sep = [@args, "", ""][2];
    lines = [];
    line = "";
    max = .element_maxlength(list) + sep.length();
    cols = len > max ? len / max | 1;
    width = len / cols - sep.length();
    col = cols;
    for part in (list) {
        col = col - 1;
        if (!col) {
            lines = lines + [line + part];
            line = "";
            col = cols;
            continue;
        }
        line = line + part.pad(width);
    }
    if (line)
        return lines + [line];
    return lines;
.

public swapsort
    arg list, [sort_by];
    var bot_elem, cur_elem, elem, compare;
    
    // note: iterative implementation allows sorts of extra-long lists
    elem = [@sort_by, 1];
    compare = [@sort_by, 'gt, 'lt][2];
    for bot_elem in [1 .. list.length()] {
        for cur_elem in [bot_elem + 1 .. list.length()] {
            if (._swap_compare(list[bot_elem], list[cur_elem], compare, elem))
                list = $list.swap(list, bot_elem, cur_elem);
        }
    }
    return list;
.

public _swap_compare
    arg elem1, elem2, compare, [elem];
    
    elem = [@elem, 1][1];
    switch (compare) {
        case 'lt:
            return elem1[elem] < elem2[elem];
        case 'gt:
            return elem1[elem] > elem2[elem];
        default:
            return 0;
    }
.

public heapsort
    arg list, [sort_by];
    var heap, sort_type, sort_elem;
    
    sort_elem = [@sort_by, 1][1];
    sort_type = [@sort_by, 'gt, 'gt][2];
    switch (sort_type) {
        case 'gt:
            heap = $small_first_heap_class.new(list, sort_elem);
        default:
            return list;
    }
    list = [];
    while (heap.length()) {
        list = heap.element(1);
        heap = heap.del(1);
    }
    return list;
.

public map_to_english
    arg list, method, [args];
    
    // because I (Lynx) am lazy
    return .to_english(.map(list, method, @args));
.

public map_to_string
    arg list, method, [args];
    
    // because I (Lynx) am lazy
    return .to_string(.map(list, method, @args));
.

public flatten
    arg list;
    var toret, elem;
    
    // [[[x], x], x]   =>   [x, x, x]
    toret = [];
    for elem in (list) {
        if (type(elem) == 'list)
            toret = toret + .flatten(elem);
        else
            toret = toret + [elem];
    }
    return toret;
.

public sum
    arg ints;
    var ret;
    
    // returns a sum of each integer in the list.
    ret = 0;
    while (ints) {
        ret = ret + ints[1];
        ints = ints.delete(1);
    }
    return ret;
.

public non_alphanumeric
    // returns nun-alphanumeric in a list of characters
    return ["!", "@", "#", "$", "%", "^", "&", "*", "(", ")", "_", "+", "-", "=", "~", "`", "'", "{", "}", "[", "]", "|", "/", "?", "\"", "\\", ",", ".", "<", ">", ";", ":", " "];
.

public numbers
    // returns a list of numbers
    return ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"];
.

public alphabet
    //returns the alphabet in a list
    return ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"];
.

public center_lines
    arg lines, width, [args];
    var output, line;
    
    output = [];
    for line in (lines)
        output = [@output, $string.center(line, width, @args)];
    return output;
.

public to_buffer
    arg [args];
    
    return (> $buffer.from_strings(@args) <);
.

public delete
    arg [args];
    
    return (> delete(@args) <);
.

public replace
    arg [args];
    
    return (> replace(@args) <);
.

public chop
    arg list, [count];
    
    // chops the last <count> elements off the list.
    // return [] if count is longer then the list.
    count = count || 1;
    return (| list.subrange(1, list.length() - count) |) || [];
.

public join
    arg l1, l2;
    var last, first;
    
    // Combines l1 and l2 by appending the first element of l2 to the last
    // of l1.
    if (type(l2) != 'list)
        l2 = [l2];
    last = (| l1.last() |) || "";
    first = (| l2[1] |) || "";
    l1 = [@l1.chop(), last + first];
    if (l2.length() > 1)
        l1 = l1 + l2.subrange(2);
    return l1;
.

public lmap
    arg list, method, [args];
    var out, x, s;
    
    //call methods for each thing in list on sender()
    out = [];
    s = sender();
    for x in (list)
        out = [@out, s.(method)(x, @args)];
    return out;
.

public length
    arg l;
    
    return listlen(l);
.

public union
    arg [args];
    
    return (> union(@args) <);
.

public omap
    arg list, object, method, [args];
    var out, obj;
    
    // calls object.method(obj, @args) for each obj in list
    out = [];
    for obj in (list)
        out = [@out, object.(method)(obj, @args)];
    return out;
.

public del
    arg list, element;
    
    return (> list.setremove(element) <);
.

public add
    arg list, element;
    
    return (> list.setadd(element) <);
.

public set_difference
    arg [args];
    var set, list, element;
    
    // Usage:  diff(set 1, set 2, ..., set n)
    // Returns all elements of set 1 that are not in sets 2..n
    if (!args)
        return [];
    set = args[1];
    for list in (args.delete(1)) {
        for element in (list)
            set = set.setremove(element);
    }
    return set;
.

public set_contains
    arg [args];
    var super, list, element;
    
    // True if the first list given is a superset of all subsequent lists.
    // False otherwise.  [] is a superset of [] and nothing else; anything is
    // a superset of [].  If only one list is given, return true.
    super = args ? args[1] | [];
    for list in (args.delete(1)) {
        for element in (list) {
            if (!(element in super))
                return 0;
        }
    }
    return 1;
.

public set_equal
    arg set1, set2;
    var element;
    
    // True if the two lists given contain the same elements.
    // False otherwise.
    while (set1) {
        element = set1[1];
        if (!element in set2)
            return 0;
        while (element in set2)
            set2 = set2.setremove(element);
        while (element in set1)
            set1 = set1.setremove(element);
    }
    return set2 == [];
.

public fold
    arg list, object, method, [args];
    var i, out;
    
    // apply object.method to a current result and the next element, return the
    // result
    if (list == [])
        return 0;
    out = list[1];
    for i in (list.subrange(2, list.length() - 1))
        out = object.(method)(out, i, @args);
    return out;
    
.

public setadd
    arg [args];
    
    return (> setadd(@args) <);
.

public intersection
    arg l1, l2;
    var i, out;
    
    // set intersection if the arguments
    out = [];
    for i in (l1) {
        if (i in l2)
            out = out.setadd(i);
    }
    return out;
.

public setremove
    arg [args];
    
    return (> setremove(@args) <);
.

public insert
    arg [args];
    
    return (> insert(@args) <);
.

public subrange
    arg [args];
    
    return (> sublist(@args) <);
.

public msort
    arg list, [keys];
    
    keys = keys ? keys[1] | list;
    if (listlen(list) != listlen(keys))
        throw(~invarg, "Invalid key list - the list lengths must be the same.");
    if (!list)
        return [];
    return (._merge_sort(list, keys))[1];
    
    // 9-25-95/21:26 Jenner ($jenner), moved from $jenner.msort
.

private _merge_sort
    arg list, keys;
    var i, j, l1, k1, l2, k2, n1, n2, n;
    
    n = listlen(list);
    if (n == 1)
        return [list, keys];
    n1 = n / 2;
    n2 = n - n1;
    l1 = ._merge_sort(list.subrange(1, n1), keys.subrange(1, n1));
    k1 = l1[2];
    l1 = l1[1];
    l2 = ._merge_sort(list.subrange(n1 + 1, n2), keys.subrange(n1 + 1, n2));
    k2 = l2[2];
    l2 = l2[1];
    list = [];
    keys = [];
    i = 1;
    j = 1;
    if (n > 30)
        pause();
    while (i <= n1 && j <= n2) {
        if (k1[i] <= k2[j]) {
            list = [@list, l1[i]];
            keys = [@keys, k1[i]];
            i = i + 1;
        } else {
            list = [@list, l2[j]];
            keys = [@keys, k2[j]];
            j = j + 1;
        }
    }
    return [[@list, @l1.subrange(i), @l2.subrange(j)], [@keys, @k1.subrange(i), @k2.subrange(j)]];
.

public prefix
    arg list, prefix;
    var out, elem;
    
    out = [];
    for elem in (list)
        out = out + [prefix + elem];
    return out;
.

public valid_objects
    arg list;
    var obj;
    
    for obj in (list) {
        if (!valid(obj))
            list = list.setremove(obj);
    }
    return list;
.



parent $libraries
object $security

var $root objname 'security
var $root created_on 796593414
var $root inited 1
var $root flags ['parameters, 'methods, 'code, 'core]
var $security valid_flags ['indestrucable, 'fertile, 'methods, 'parameters, 'code]

public valid_flags
    return valid_flags;
.

public index
    arg flag;
    
    if (!flag in valid_flags)
        throw(~type, "Invalid flag.");
    return flag in valid_flags;
.



parent $libraries
object $help_lib

var $root objname 'help_lib
var $root created_on 805931416
var $root inited 1
var $root flags ['parameters, 'methods, 'code, 'core]
var $help_lib default_node $help_summary
var $help_lib default_index $help_index_root

public default_node
    return default_node;
.

public default_index
    return default_index;
.

public find_help_node
    arg what, type;
    var pos, node, current, indices, l;
    
    current = (| sender().current_node() |) || .default_node();
    switch (type) {
        case 'index:
            indices = sender().help_indices();
            l = indices.length();
            while (l && !node) {
                node = indices[l].find_help_node(what);
                l = l - 1;
            }
            if (node)
                return node;
            else
                throw(~nonode, "Unable to find help on \"" + what + "\".");
        case 'link:
            for node in (current.keys().links()) {
                if ($string.match_begin(node, what))
                    return (current.links())[node];
            }
            throw(~nonode, "No node link \"" + what + "\" found on current node.");
        default:
            // Up/Down nodes need to be redone for groups
    }
    return 0;
.

public history_cap
    return 15;
.

public parse_reference
    arg str;
    var node, current, indices, l, links;
    
    if (str[1] in ["$", "#"]) {
        node = (> $object_lib.to_dbref(str) <);
        if (!node.has_ancestor($help_node))
            throw(~nonode, "\"" + str + "\" is not descended from $help_node.");
        return node;
    }
    links = ((| sender().current_node() |) || .default_node()).links();
    for node in (links.keys()) {
        if ($string.match_begin(node, str))
            return links[node];
    }
    indices = (| sender().help_indices() |) || [.default_index()];
    node = 0;
    l = indices.length();
    while (l && !node) {
        node = indices[l].find_help_node(str);
        l = l - 1;
    }
    return node || throw(~nonode, "Unable to find help on \"" + str + "\".");
.

public group_nodes_in_html
    arg nodes, noemph, [args];
    var name, names, n;
    
    names = [];
    for n in (nodes) {
        if (n in noemph)
            name = n.name();
        else
            name = .node_name_in_html(n);
        names = [@names, name];
    }
    return names.to_english(@args);
.

public node_name_in_html
    arg node;
    
    return "<a href=\"/bin/help?" + node.objname() + "\">" + node.name() + "</a>";
.



parent $libraries
object $ctext_lib

var $root objname 'ctext_lib
var $root created_on 799277513
var $root inited 1
var $root flags ['parameters, 'methods, 'code, 'core]



parent $libraries
parent $mail_root
object $mail_lib

var $root inited 1
var $root objname 'mail_lib
var $root created_on 796268969
var $root indestructable 1
var $root flags ['parameters, 'methods, 'code, 'core]
var $mail_lib mail_system [$mail_message, $mail_list, $mail_ui, $mail_lib]

public mail_name
    arg obj;
    
    if (!obj.has_ancestor($mail_list))
        throw(~type, "Object is not a child of $mail_recipient");
    if (obj.has_ancestor($user))
        return obj.namef().replace(" ", "-");
    return "*" + obj.namef().replace(" ", "-");
.

public has_mail_perms
    arg [args];
    var obj;
    
    for obj in (args) {
        if (!(obj in mail_system) && !$sys.is_system(obj))
            return 0;
    }
.

public match_mail_recipient
    arg name;
    var mname, list;
    
    catch ~namenf, ~objnf {
        if (name[1] == "*") {
            list = "list";
            return $mail_db.find(name.subrange(2));
        } else {
            list = "user";
        }
        return sender().find_object(name, 'environment, 'user, 'grasp);
    } with handler {
        throw(~listnf, "No " + list + " found by the name \"" + name + "\".");
    }
.

public range_to_actual
    arg r, current;
    var start, end;
    
    if (type(r[1]) == 'symbol) {
        switch (r[1]) {
            case 'end:
                if (type(r[2]) != 'symbol)
                    throw(~range, "Backwards range.");
                start = current['list].mail().length();
            case 'start:
                start = 1;
            case 'current:
                start = current['location] in current['list].mail();
        }
    } else {
        start = r[1];
    }
    if (type(r[2]) == 'symbol) {
        switch (r[2]) {
            case 'end:
                end = current['list].mail().length();
            case 'single:
                end = start;
            case 'start:
                throw(~range, "Backwards range.");
            case 'current:
                end = current['location] in current['list].mail();
        }
    } else {
        start = r[2];
    }
    if (start > end)
        throw(~range, "Backwards range.");
    return [start, end];
.



parent $libraries
object $time_lib

var $root inited 1
var $root objname 'time_lib
var $root created_on 796268969
var $root flags ['parameters, 'methods, 'code, 'core]



parent $libraries
object $data_lib

var $root inited 1
var $root objname 'data_lib
var $root created_on 796268969
var $root flags ['parameters, 'methods, 'code, 'core]

public unparse
    arg data, [unparse];
    var str, element, association, pos, method;
    
    unparse = [@unparse, #[]][1];
    switch (type(data)) {
        case 'integer, 'float:
            return tostr(data);
        case 'string:
            method = (| unparse['string] |) || 'toliteral;
            return $string.(method)(data);
        case 'symbol:
            return toliteral(data);
        case 'error:
            return toliteral(data);
        case 'buffer:
            return toliteral(data);
        case 'dbref:
            method = (| unparse['dbref] |) || ['objname];
            return $object_lib.get_name(data, @method);
        case 'list:
            if (!data)
                return "[]";
            str = "[";
            for element in (data.subrange(1, data.length() - 1)) {
                str = str + $data_lib.unparse(element);
                str = str + ", ";
            }
            str = str + $data_lib.unparse(data[data.length()]);
            return str + "]";
        case 'dictionary:
            if (!data)
                return "#[]";
            str = "#[";
            for association in (data) {
                str = str + $data_lib.unparse(association);
                str = str + ", ";
            }
            return str.subrange(1, str.length() - 2) + "]";
        case 'frob:
            catch any {
                return data.unparse();
            } with handler {
                return toliteral(data);
            }
    }
.

public verify_type
    arg value, type;
    var elem, type_elem, ok_flag;
    
    // returns true if type(value) is type, or in the case 
    // of lists and dicts it checks the elements
    //
    // If type is a list or dict, the elements it contains are possible types for value
    // for example: .verify_value([['foo], 1],['integer, 'list, ['symbol]]) => 1
    // To indicate that you don't care what the elements of a dict or list are,
    // simply use 'list or 'dictionary.
    if (type(value) == type)
        return 1;
    if (type(type) in ['dictionary, 'list] && type(type) == type(value)) {
        for elem in (value) {
            ok_flag = 0;
            for type_elem in (type) {
                if (.verify_type(elem, type_elem)) {
                    ok_flag = 1;
                    break;
                }
                if (!ok_flag)
                    return 0;
            }
        }
        return 1;
    }
    
    // value is not of type, or values elements are not of @type, or etc
    return 0;
.



parent $libraries
object $symbol

var $root objname 'symbol
var $root created_on 811822782
var $root inited 1
var $root flags ['parameters, 'methods, 'code, 'core]

public to_string
    arg sym;
    
    return tostr(sym);
.



parent $misc
object $tree

var $root child_index 5
var $root fertile 1
var $root inited 1
var $root objname 'tree
var $root created_on 796268969
var $root indestructable 1
var $root flags ['parameters, 'methods, 'code, 'fertile, 'core]
var $tree parents_by_tree 0
var $tree children_by_tree 0
var $tree tree_parents 0
var $tree tree_children 0

public parent_nodes
    arg node;
    
    return tree_parents[node];
.

public child_nodes
    arg node;
    
    return tree_children[node];
.

public unlink_nodes
    arg parent, child;
    var parents, kids;
    
    (> .perms(sender(), 'writers) <);
    parents = .parent_nodes(child);
    kids = .child_nodes(parent);
    parents = parents.setremove(parent);
    kids = kids.setremove(child);
    tree_children = tree_children.add(parent, kids);
    tree_parents = tree_parents.add(child, parents);
.

public _del_links
    arg n, node;
    var nodes;
    
    //delete links from n to node
    nodes = tree_parents[n];
    nodes = nodes.delete(node in nodes);
    tree_parents = tree_parents.add(n, nodes);
    nodes = tree_children[n];
    nodes = nodes.delete(node in nodes);
    tree_children = tree_children.add(n, nodes);
.

public link_nodes
    arg parent, child;
    var parents, kids;
    
    (> .perms(sender(), 'writers) <);
    parents = .parent_nodes(child);
    kids = .child_nodes(parent);
    parents = parents.setadd(parent);
    kids = kids.setadd(child);
    tree_children = tree_children.add(parent, kids);
    tree_parents = tree_parents.add(child, parents);
.

root init_tree
    tree_parents = #[];
    tree_children = #[];
.

root uninit_tree
    parents_by_tree = 0;
    children_by_tree = 0;
.

public del_node
    arg node;
    var n, kids, parents, nodes;
    
    (> .perms(sender(), 'writers) <);
    kids = tree_children[node];
    tree_children = tree_children.del(node);
    parents = tree_parents[node];
    tree_parents = tree_parents.del(node);
    for n in (kids) {
        nodes = tree_parents[n];
        nodes = nodes.delete(node in nodes);
        tree_parents = tree_parents.add(n, nodes);
    }
    for n in (parents) {
        nodes = tree_children[n];
        nodes = nodes.delete(node in nodes);
        tree_children = tree_children.add(n, nodes);
    }
.

public add_node
    arg node;
    
    (> .perms(sender(), 'writers) <);
    tree_children = tree_children.add(node, []);
    tree_parents = tree_parents.add(node, []);
.



parent $misc
object $db

var $root inited 1
var $root objname 'db
var $root created_on 796268969
var $root indestructable 1
var $root flags ['parameters, 'methods, 'code, 'core]
var $db database #[]

root init_db
    database = #[];
.

public database
    return database;
.

public value_changed
    arg key, new_value;
    
    // change the value of a key.
    (> .remove(key) <);
    (> .insert(key, new_value) <);
.

public remove
    arg key;
    
    // remove a key/value from the database
    //THIS: is breaking the user_db (quick comment fix :)
    // .perms(sender(), 'writer);
    database = database.del(key);
.

public exact_match
    arg key;
    var match;
    
    // get an exact match of a key, return the value
    match = (| database[key] |);
    if (match == ~keynf)
        throw(~matchnf, "No object by that key exists in the database.");
    return match;
.

public match_begin
    arg key;
    var matches, entry;
    
    // use match_begin of the key, return the value
    matches = [(| .exact_match(key) |)];
    if (!matches[1]) {
        matches = [];
        for entry in (database) {
            if ($string.match_begin(entry[1], key))
                matches = [@matches, entry[2]];
        }
    }
    if (matches) {
        if (matches.length() == 1)
            return matches[1];
        else
            throw(~ambig, "More than one object matches that key.", matches);
    } else {
        throw(~matchnf, "No entries in the database match that key.");
    }
.

public insert
    arg key, value;
    
    // insert a key/value to the database
    //  .perms(sender(), 'writer);
    database = database.add(key, value);
.

root uninit_db
    database = 0;
.

public key_changed
    arg old_key, new_key;
    var val;
    
    // change the value of a key.
    val = (> database[old_key] <);
    .remove(old_key);
    .insert(new_key, val);
.



parent $misc
object $user_parsers

var $root child_index 9
var $root fertile 1
var $root trusted []
var $root inited 1
var $root objname 'user_parsers
var $root created_on 796268969
var $root indestructable 1
var $root flags ['parameters, 'methods, 'code, 'fertile, 'core]

public parse
    arg user, str, next_parser, [other_parsers];
    
    // Minimum parser routine.
    return next_parser.parse(user, str, @other_parsers);
.



parent $misc
object $trie

var $root objname 'trie
var $root created_on 800074237
var $root inited 1
var $root flags ['parameters, 'methods, 'code, 'core]
var $trie data 0

public find_key
    arg trie, key;
    var char, n;
    
    if (!key) {
        if (!trie[1])
            throw(~ambig, "Trie: ambiguous match.");
        else
            return trie[1];
    }
    if (!n = key[1] in trie[2])
        throw(~keynf, "Trie: key not found.");
    else
        return (> .find_key(key.subrange(2), trie[n + 2]) <);
.

public add
    arg trie, key, [values];
    var n;
    
    if (!key)
        return trie.replace(1, [key, @values]);
    if (!n = key[1] in trie[2])
        return [@trie.replace(2, trie[2] + key[1]), .add([0, ""], key.subrange(2), @values)];
    return trie.replace(n + 2, .add(trie[n + 2], key.subrange(2), @values));
.

public del
    arg trie, key;
    var n;
    
    if (!key)
        return trie.replace(1, 0);
    if (!n = key[1] in trie[2])
        return trie;
    return trie.replace(n + 2, .del(trie[n + 2], key.subrange(2)));
.



parent $misc
object $filters

var $root child_index 1
var $root inited 1
var $root objname 'filters
var $root created_on 796268969
var $root indestructable 1
var $root flags ['parameters, 'methods, 'code, 'core]

public compress
    arg input;
    var e, output;
    
    // only goes 1 element deep--sorry, anybody sending anything past that will
    // be shot.
    output = [];
    if (type(input) == 'list) {
        for e in (input) {
            if (type(e) == 'list)
                output = [@output, @e];
            else
                output = [@output, e];
        }
    } else {
        output = [input];
    }
    return output;
.



parent $misc
object $lock_parser

var $root inited 1
var $root objname 'lock_parser
var $root created_on 796268969
var $root indestructable 1
var $root flags ['parameters, 'methods, 'code, 'core]

public parse
    arg s, env;
    var stack, lock, n, m, obj, len, reg;
    
    stack = [];
    s = " " + s;
    s = s.replace(" or ", " || ");
    s = s.replace(" and ", " || ");
    s = s.replace(" not ", " !");
    while (1) {
        // Look for valid prefixes.
        while (1) {
            while (s && s[1] == " ")
                s = s.subrange(2);
            if (!s)
                throw(~parse, "String ends unexpectedly.");
            if (s[1] == "(") {
                stack = stack + ['open];
                s = s.subrange(2);
            } else if (s[1] == "!") {
                stack = stack + ['not];
                s = s.subrange(2);
            } else {
                break;
            }
        }
    
        // Look for an object name or tag
        for n in [1 .. s.length()] {
            if (s[n] in ")&|") {
                n = n - 1;
                break;
            }
        }
        m = n;
        while (m && s[m] == " ")
            m = m - 1;
        if (!m)
            throw(~parse, "Null object obj_name.");
    
        // try and match it
        if (lock = (| env.match_environment(s.subrange(1, m)) |)) {
            obj = (> env.match_environment(s.subrange(1, m)) <);
            lock = $object_lock_frob.new(obj);
        } else {
            switch (s.subrange(1, m)) {
                case "any", "true", "anybody":
                    lock = $true_lock_frob.new();
                case "none", "false", "nobody":
                    lock = $false_lock_frob.new();
                default:
                    throw(~parse, "Invalid lock tag \"" + s.subrange(1, m) + "\"");
            }
        }
        stack = stack + [lock];
        s = s.subrange(n + 1);
    
        // Loop until no more reduction to be done.
        while (1) {
            // Process negations, ands, ors.
            while (1) {
                len = stack.length();
                if (len < 2)
                    break;
                if (stack[len - 1] == 'not) {
                    lock = $not_lock_frob.new(stack[len]);
                    stack = stack.subrange(1, len - 2) + [lock];
                } else if (stack[len - 1] == 'and) {
                    lock = $and_lock_frob.new(stack[len - 2], stack[len]);
                    stack = stack.subrange(1, len - 3) + [lock];
                } else if (stack[len - 1] == 'or) {
                    lock = $or_lock_frob.new(stack[len - 2], stack[len]);
                    stack = stack.subrange(1, len - 3) + [lock];
                } else {
                    break;
                }
            }
    
            // Close parens, if necessary; otherwise stop.
            if (!s || s[1] != ")")
                break;
            while (s && s[1] == ")") {
                len = stack.length();
                if (len < 2 || stack[len - 1] != 'open)
                    throw(~parse, "Misplaced right parenthesis.");
                stack = stack.subrange(1, len - 2) + [stack[len]];
                s = s.subrange(2);
                while (s && s[1] == " ")
                    s = s.subrange(2);
            }
        }
    
        // Are we done?
        if (!s) {
            if (stack.length() > 1)
                throw(~parse, "Unmatched left parentheses.");
            return stack[1];
        }
    
        // No, we're at a conjunction.
        if (s[1] == "&") {
            stack = stack + ['and];
            s = s.subrange(3);
        } else if (s[1] == "|") {
            stack = stack + ['or];
            s = s.subrange(3);
        } else {
            throw(~parse, "Illegal character following right parenthesis.");
        }
    }
.



parent $utilities
object $cml2

var $root objname 'cml2
var $root child_index 3
var $root created_on 806017887
var $root inited 1
var $root flags ['parameters, 'methods, 'code, 'core]



parent $utilities
object $world

var $root fertile 1
var $root inited 1
var $root objname 'world
var $root created_on 796268969
var $root flags ['parameters, 'methods, 'code, 'fertile, 'core]
var $world starting_place 0

public daylight
    // should eventually return something like 'day 'night 'dusk 'dawn
    return 'day;
.

public starting_place
    return starting_place || $body_cave;
.



parent $utilities
object $root_evaluator

var $root child_index 2
var $root fertile 1
var $root inited 1
var $root objname 'root_evaluator
var $root created_on 796268969
var $root flags ['parameters, 'methods, 'code, 'fertile, 'core]
var $root_evaluator append_str "_stmt"
var $has_messages messages 0

public eval_ctext
    arg vars, term;
    var vars;
    
    // message is the message to be evaluated.
    // times is a list of the symbols that should be processed.
    // times = [] processes all symbols.
    if (type(term) != 'list)
        throw(~type, "Term must be a list. Was " + $data_lib.unparse(term));
    if ('time in vars.keys()) {
        if (type(vars['time]) != 'symbol)
            throw(~type, "Time must be a symbol.");
    } else {
        vars = vars.add('time, 'pre);
    }
    vars = vars.add('force, 1).add('delay, 0).add('debug, 0);
    vars = ._eval_ctext(vars, term);
    if ('process in vars.keys())
        return vars;
    else
        return $ctext_frob.new(vars['result]);
.

public apply
    arg vars, list, method, [args];
    var l, output;
    
    // call method once for each element in list.
    // the element is passed as the first argument.
    output = [];
    if (args) {
        for l in (list) {
            vars = .(method)(vars, [l, @args]);
            output = [@output, vars['result]];
        }
    } else {
        for l in (list) {
            vars = .(method)(vars, l);
            output = [@output, vars['result]];
        }
    }
    if (output.length() > 1)
        return vars.add('result, ['list_type, output]);
    else if (output.length() == 1)
        return vars.add('result, output[1]);
    else
        return vars.add('result, ['list_type, []]);
.

public list_type
    arg vars, term;
    var t, output, output2;
    
    output = [];
    for t in (term) {
        if (type(t[1]) == 'list)
            t = t[1];
        vars = ._eval_ctext(vars, t);
        output = [@output, vars['result]];
    }
    return vars.add('result, ['list_type, output]);
.

public parse_command
    arg text;
    var output, ret_val, term, word, final, first_term, final_args, token;
    
    output = [];
    term = [];
    word = "";
    while (text) {
        ret_val = text.split_on_next(["\"", "{", "%", "&", "[", " ", "}"]);
        word = ret_val[1];
        token = ret_val[2];
        text = ret_val[3];
        switch (token) {
            case "{":
                output = ._handle_word(output, term);
                ret_val = .parse_command(text);
                output = [@output, ret_val[1]];
                text = ret_val[2];
            case "[":
                output = ._handle_word(output, term);
                ret_val = .parse_list(text);
                output = [@output, ret_val[1]];
                text = ret_val[2];
            case "%":
                output = ._handle_word(output, term);
                ret_val = .parse_varref(text);
                output = [@output, ret_val[1]];
                text = ret_val[2];
            case "\"":
                output = ._handle_word(output, term);
                ret_val = .parse_string(text);
                output = [@output, ret_val[1]];
                text = ret_val[2];
            case " ":
                output = ._handle_word(output, word);
            case "}":
                output = ._handle_word(output, word);
                if (type(output[1]) != 'list)
                    output = [output];
                first_term = output[1];
                if (first_term[1] == 'string_type) {
                    first_term = tosym(first_term[2] + "_stmt");
                    final_args = [];
                } else {
                    first_term = 'eval_stmt;
                    final_args = [first_term];
                }
                if (output.length() > 1)
                    final_args = [@(final_args + output.subrange(2))];
                return [[first_term, final_args], text];
        }
    }
.

public normalize
    arg args;
    var key, new;
    
    // turns ctext style data into Coldc data
    switch (args[1]) {
        case 'dictionary_type:
            new = #[];
            for key in (args[2].keys())
                new = new.add(key, .normalize(args[2][key]));
            return new;
        case 'list_type:
            new = [];
            for key in (args[2])
                new = [@new, .normalize(key)];
            return new;
        default:
            return args[2];
    }
.

public fix_values
    arg stuff;
    var key, new;
    
    switch (type(stuff)) {
        case 'dictionary:
            new = #[];
            for key in (stuff)
                new = new.add(key[1], .fix_values(key[2]));
            return new;
        case 'list:
            new = [];
            for key in (stuff)
                new = [@new, .fix_values(key)];
            return ['list_type, new];
        case 'symbol:
            return ['symbol_type, stuff];
        case 'string:
            return ['string_type, stuff];
        case 'integer:
            return ['integer_type, stuff];
        case 'dbref:
            return ['dbref_type, stuff];
    }
.

public _truth
    arg value;
    var result;
    
    switch (value[1]) {
        case 'symbol_type:
            return 1;
        case 'integer_type:
            return value[2];
        case 'string_type:
            if (value[2] == "0" || value[2] == "")
                return 0;
            else
                return 1;
        case 'list_type:
            return value[2].length();
        default:
            return ">>ERROR: Argument to condtional not a value<<";
    }
.

public parse_list
    arg text;
    var output, ret_val, term, word, token, int;
    
    output = [];
    term = [];
    word = "";
    while (text) {
        ret_val = text.split_on_next(["[", "]", "(", "\"", "%", "&", " "]);
        word = ret_val[1];
        token = ret_val[2];
        text = ret_val[3];
        switch (token) {
            case "(":
                output = ._handle_word(output, word);
                vars = .parse_command(text);
                output = [@output, vars['result]];
                text = vars['text];
            case "[":
                output = ._handle_word(output, word);
                vars = .parse_list(text);
                output = [@output, vars['result]];
                text = vars['text];
            case "%":
                output = ._handle_word(output, word);
                vars = .parse_varref(text);
                output = [@output, vars['result]];
                text = vars['result];
            case "\"":
                output = ._handle_word(output, word);
                ret_val = .parse_string(text);
                output = [@output, vars['result]];
                text = vars['result];
            case " ":
                output = ._handle_word(output, word);
            case "]":
                output = ['list_type, ._handle_word(output, word)];
                vars = vars.add('text, text);
                return vars.add('result, output);
        }
    }
.

public parse_string
    arg text;
    var output, word, ret_val, is_text, token;
    
    output = [];
    word = "";
    is_text = 0;
    while (text) {
        ret_val = text.split_on_next(["\"", "{", "%", "&"]);
        word = ret_val[1];
        token = ret_val[2];
        text = ret_val[3];
        switch (token) {
            case "{":
                output = ._handle_word(output, word);
                ret_val = .parse_command(text);
                output = [@output, ret_val[1]];
                text = ret_val[2];
                is_text = 1;
            case "%":
                output = ._handle_word(output, word);
                ret_val = .parse_varref(text);
                output = [@output, ret_val[1]];
                text = ret_val[2];
                is_text = 1;
            case "&":
                output = ._handle_word(output, word);
                ret_val = .parse_charref(text);
                output = [@output, ret_val[1]];
                text = ret_val[2];
                is_text = 1;
            case "\"":
                output = ._handle_word(output, word);
                if (is_text)
                    output = ['text_stmt, output];
                if (output.length() == 1)
                    output = output[1];
                return [output, text];
        }
    }
    return [output, text];
.

public parse_charref
    arg text;
    var pos;
    
    pos = ";" in text;
    return [['char_type, text.subrange(1, pos - 1)], text.subrange(pos + 1)];
.

public parse_varref
    arg text;
    var output, ret_val, word, token;
    
    output = [];
    word = "";
    while (text) {
        ret_val = text.split_on_next(["{", "%", "&", ";"]);
        word = ret_val[1];
        token = ret_val[2];
        text = ret_val[3];
        switch (token) {
            case "{":
                output = ._handle_word(output, word);
                ret_val = .parse_command(length, text);
                output = [@output, @ret_val[1]];
                text = ret_val[2];
            case "%":
                output = ._handle_word(output, word);
                ret_val = .parse_varref(length, text);
                output = [@output, @ret_val[1]];
                text = ret_val[2];
            case "&":
                output = ._handle_word(output, word);
                ret_val = .parse_charref(length, text);
                output = [@output, @ret_val[1]];
                text = ret_val[2];
            case ";":
                output = ._handle_word(output, word);
                return [['get_stmt, output], text];
        }
    }
.



parent $utilities
object $has_hooks

var $root objname 'has_hooks
var $root child_index 1
var $root fertile 1
var $root created_on 809174672
var $root inited 1
var $root flags ['parameters, 'methods, 'code, 'fertile, 'core]
var $has_hooks hooks #[]

public hooks
    return hooks.keys();
.

public add_hook
    arg hook;
    
    (> .perms(sender(), 'writer) <);
    hooks = hooks.add(hook, []);
.

public del_hook
    arg hook;
    
    (> .perms(sender(), 'writer) <);
    hooks = hooks.del(hook);
.

public set_hook
    arg hook;
    var ret;
    
    if (sender() in hooks[hook])
        return 1;
    catch ~methodnf {
        ret = .(tosym("accept_" + tostr(hook) + "_hook"))(sender());
    } with handler {
        ret = 1;
    }
    if (!ret)
        throw(~perm, "Hook not allowed.");
    hooks = hooks.add(hook, [@hooks[hook], sender()]);
    (| .(tosym("record_" + tostr(hook) + "_hook"))(sender()) |);
.

public unset_hook
    arg hook;
    
    if (sender() in hooks[hook]) {
        hooks = hooks.add(hook, hook.delete(sender() in hooks[hook]));
        (| .(tosym("abort_" + tostr(hook) + "_hook"))(sender()) |);
    }
.

protected call_hook
    arg hook, [args];
    var objects, method, o;
    
    //Notify all hooked objects that the hook has been triggered normally.
    objects = hooks[hook];
    method = tosym("hook_" + tostr(hook) + "_triggered");
    for o in (objects)
        (| o.(method)('normal, @args) |);
.

public remove_hook
    arg hook;
    
    (> .perms(sender(), 'writers) <);
    hooks = hooks.add(hook, hooks[hook].delete(sender() in hooks[hook]));
.

public init_has_hooks
    hooks = #[];
.



parent $utilities
object $lag_watcher

var $root inited 1
var $root objname 'lag_watcher
var $root created_on 796268969
var $root flags ['parameters, 'methods, 'code, 'core]
var $lag_watcher lags [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
var $lag_watcher last_time 813386635

public startup
    // Called by $sys.startup()
    (> .perms(sender(), 'manager) <);
    lags = [];
    last_time = time();
    $heart.add_heartbeat(14);
.

public update
    arg lag;
    
    // Called by .pulse() every 15 minutes
    (> .perms(sender(), 'this) <);
    if (type(lags) != 'list)
        lags = [];
    while (lags.length() > 9)
        lags = lags.delete(1);
    lag = lag > 15 ? lag - 15 | 0;
    lags = [@lags, lag];
.

public lag_str
    return "Current server lag is " + .lag() + " seconds.";
.

public lag
    var lag, total, weight, lag_i, lag_d;
    
    // the current lag floated
    total = 0;
    weight = 0;
    for lag in (lags) {
        weight = weight + 1;
        total = total + lag * weight;
    }
    lag_i = total / 55;
    lag_d = tostr((total - lag_i * 55) * 10 / 55).subrange(1, 1);
    return tostr(lag_i) + "." + lag_d;
.

public verbose_lag_str
    // returns lag as a string with verbosity
    return ["Current server lag is " + .lag() + " seconds.", $data_lib.unparse(lags)];
.

public value
    var lag, total, weight, lag_i;
    
    // unparsed lag value
    total = 0;
    weight = 0;
    for lag in (lags) {
        weight = weight + 1;
        total = total + lag * weight;
    }
    lag_i = total / 55;
    return lag_i;
.

public pulse
    var lag;
    
    // Called by $heart every 15 mins
    (> .perms(sender(), $heart) <);
    lag = time() - last_time;
    last_time = time();
    .update(lag);
.



parent $utilities
object $channels

var $root inited 1
var $root objname 'channels
var $root created_on 796268969
var $root flags ['parameters, 'methods, 'code, 'core]

public announce
    arg channel, text, [args];
    var user, name, line, exempt, listeners;
    
    if (!.can_send_to(channel, caller(), sender()))
        throw(~perm, "Sorry, unable to send to that channel.");
    name = [@args, 'objname][1];
    if (!name && !(channel in ['login, 'System]))
        name = 'objname;
    exempt = [@args, [], []][2];
    listeners = .listeners(channel).set_difference(exempt);
    if (args.length() > 2)
        args = args.subrange(3);
    else
        args = [];
    line = "<" + tostr(channel);
    if (name)
        line = line + " - " + sender().(name)();
    line = line + ": " + text + ">";
    for user in (listeners) {
        if ((| user.connected() |))
            user.tell(line, 'channel, channel, @args);
    }
.

public listeners
    arg channel;
    
    switch (channel) {
        case 'System:
            return $programmer.descendants();
        case 'admin:
            return $admin.children();
        default:
            return $user_db.connected();
    }
.

public can_send_to
    arg channel, caller, sender;
    
    switch (channel) {
        case 'System, 'all:
            if ($sys.is_system(sender))
                return 1;
        case 'login:
            if (caller == $user)
                return 1;
        default:
            return 1;
    }
    return 0;
.



parent $utilities
object $places

var $root inited 1
var $root objname 'places
var $root created_on 796268969
var $root flags ['parameters, 'methods, 'code, 'core]
var $places default_place $nowhere
var $places exit_starting_place $void
var $places starting_place $the_pit
var $places default_new_place $place
var $places coordinate_shortcuts #[["n?orth", [0, 0]], ["s?outh", [180, 0]], ["e?ast", [90, 0]], ["w?est", [270, 0]], ["ne|northeast", [45, 0]], ["se|southeast", [135, 0]], ["nw|northwest", [225, 0]], ["sw|southwest", [315, 0]], ["d?own", [-1, -90]], ["u?p", [-1, 90]]]
var $places known_realms [$realm_of_creation]
var $places build_hints #[[1, <$ctext2_frob, [["This is the do-it-all system for building places. At any time you can enter \"@abort\" to abort building. To turn off these hints \"@set experienced\" in your regular command environment.", <$ctext_format, ["hr", #[], #[], [], 'do_hr]>], #[['line, 1], ['this, $places], ['time, 'pre], ['sender, $places], ['evaluator, $cml2_base_eval]]]>], [2, <$ctext2_frob, [["The specified destination does not exist, therefore a new one will be created with the name you specified.", <$ctext_format, ["hr", #[], #[], [], 'do_hr]>], #[['line, 1], ['this, $places], ['time, 'pre], ['sender, $places], ['evaluator, $cml2_base_eval]]]>], [3, <$ctext2_frob, [["Name aliases can be specified on the same line as the name. This is done by seperating them from the name with commas. Any number of aliases can be specified in this manner (such as \"Name, alias, alias, alias\"). Names types can be defined by appending ", <$ctext_format, ["tt", #[], #[], ["+type"], 'do_tt]>, ", where ", <$ctext_format, ["tt", #[], #[], ["type"], 'do_tt]>, " is one of ", <$ctext_format, ["tt", #[], #[], ["proper"], 'do_tt]>, ", ", <$ctext_format, ["tt", #[], #[], ["unique"], 'do_tt]>, " or ", <$ctext_format, ["tt", #[], #[], ["normal"], 'do_tt]>, ". Not specifying a type defaults to ", <$ctext_format, ["tt", #[], #[], ["normal"], 'do_tt]>, ".", <$ctext_format, ["hr", #[], #[], [], 'do_hr]>], #[['line, 1], ['this, $places], ['time, 'pre], ['sender, $places], ['evaluator, $cml2_base_eval]]]>], [4, <$ctext2_frob, [["Realms are used to keep locations in relation with each other. To get a list of commonly known realms type ", <$ctext_format, ["tt", #[], #[], ["@realms"], 'do_tt]>, ".", <$ctext_format, ["hr", #[], #[], [], 'do_hr]>], #[['line, 1], ['this, $places], ['time, 'pre], ['sender, $places], ['evaluator, $cml2_base_eval]]]>], [5, <$ctext2_frob, [["Coordinates are used to define a basic relation between locations by pointing in the direction each place is. They use the radial/azimuth system. More help on Coordinates can be found in help under ", <$ctext_format, ["tt", #[], #[], ["places"], 'do_tt]>, ". For now it may be easier to use a coordinate shorcut (such as ", <$ctext_format, ["tt", #[], #[], ["up"], 'do_tt]>, "). To get a list of coordinate shortcuts type ", <$ctext_format, ["tt", #[], #[], ["@shortcuts"], 'do_tt]>, " now. Note: coordinates are automatically inverted for return exits (so if you specify ", <$ctext_format, ["tt", #[], #[], ["down"], 'do_tt]>, " the return exit will use the coordinates of ", <$ctext_format, ["tt", #[], #[], ["up"], 'do_tt]>, ").", <$ctext_format, ["hr", #[], #[], [], 'do_hr]>], #[['line, 1], ['this, $places], ['time, 'pre], ['sender, $places], ['evaluator, $cml2_base_eval]]]>]]

public place
    arg which;
    
    which = tosym(tostr(which) + "_place");
    return (> get_var(which) <);
.

public is_place
    arg obj;
    
    if (!obj.has_ancestor($place))
        throw(~place, "Object \"" + obj.namef('ref) + "\" is not a place.");
.

public set_place
    arg which, obj;
    
    .perms(sender(), 'manager);
    (> .is_place(obj) <);
    if (!(which in .parameters()))
        throw(~place, toliteral(which) + " is not a valid entry try one of: " + toliteral(.parameters()));
    set_var(which, obj);
.

public coordinates
    arg str;
    var x;
    
    for x in (coordinate_shortcuts) {
        if ($string.match_template(x[1], str))
            return x[2];
    }
    throw(~coordnf, "Unable to find coordinate shortcut for \"" + str + "\".");
.

public coordinate_shortcuts
    return coordinate_shortcuts;
.

public valid_coordinates
    arg radial, azimuth;
    
    if (radial > 360 || radial < -1)
        throw(~invcoord, "Radial coordinate must be from -1 to 360 degrees.");
    if (azimuth > 90 || azimuth < -90)
        throw(~invcoord, "Azimuth coordinate must be from 90 to -90 degrees.");
.

public invert_coordinates
    arg radial, azimuth;
    
    radial = radial + 180;
    if (radial > 360)
        radial = radial - 360;
    if (azimuth > 0)
        azimuth = -azimuth;
    else
        azimuth = abs(azimuth);
    return [radial, azimuth];
.

public known_realms
    return known_realms;
.

public match_realm
    arg str;
    var r;
    
    for r in ($realms_frob.descendants()) {
        if (r.match_name(str))
            return r;
    }
    return (| $object_lib.to_dbref(str) |) || 0;
.

public add_known_realm
    arg obj;
    
    (> .perms(sender()) <);
    known_realms = [@known_realms, obj];
.

public del_known_realm
    arg obj;
    
    (> .perms(sender()) <);
    known_realms = known_realms.setremove(obj);
.

public del_build_hint
    arg hint_key;
    
    (> .perms(sender()) <);
    build_hints = build_hints.add(hint_key);
.

public build_hint
    arg hint;
    
    return build_hints[hint];
.

public build_hints
    return build_hints;
.

public add_build_hint
    arg hint_key, hint_text;
    
    (> .perms(sender()) <);
    build_hints = build_hints.add(hint_key, hint_text);
.



parent $utilities
object $motd

var $root inited 1
var $root objname 'motd
var $root created_on 796268969
var $root flags ['parameters, 'methods, 'code, 'core]
var $motd server_name "ColdCore"
var $motd notes []
var $motd server_title "Virtual Environment Server"
var $motd connect_help ["Connection HELP", "===============", "", "Connecting as a guest:    'connect-guest <name> <email>'", "              Example:    'connect-guest John Doe johnd@site.usa.com'", "", "Connecting as a user:     'connect <name> <password>'", "             Example:     'connect John Doe mypassword'", "", "Quitting (this screen):   '@quit'   or   'quit'", "", "Connected User's Listing: '@who'    or   'who'"]

public build
    arg [args];
    var output, out;
    
    output = [];
    if (!args)
        args = ['long, 'quote];
    if (args[1] == 'default)
        args = ['name, "", 'title, "", "", 'quote, "", 'notes, "", 'admins, 'connected, 'core_version, 'driver_version];
    while (args) {
        if (type(args[1]) == 'string) {
            output = output + [""];
        } else {
            switch (args[1]) {
                case 'long:
                    args = args.delete(1);
                    args = ['title, 'long_name] + args;
                    continue;
                case 'short:
                    args = args.delete(1);
                    args = ['title, 'name] + args;
                    continue;
                case 'title:
                    out = $string.center(server_title, 79);
                    output = output + [out];
                case 'name:
                    out = $string.center("+ " + server_name + " +", 79);
                    output = output + [out];
                case 'notes:
                    out = $list.center_lines(notes, 79);
                    output = output + out;
                case 'quote:
                    out = $list.center_lines($code_lib.random_quote(), 79);
                    output = output + out;
                case 'admins:
                    out = $list.to_english($list.map($sys.admins(), 'namef));
                    out = $string.center("Administrators: " + out, 79);
                    output = output + [out];
                case 'connected:
                    out = "Currently Connected users: ";
                    out = out + tostr($user_db.connected().length());
                    out = $string.center(out, 79);
                    output = output + [out];
                case 'version:
                    args = args.delete(1);
                    args = ['driver_version, 'core_version] + args;
                    continue;
                case 'driver_version:
                    out = "Driver: " + $sys.server_info('driver_version, 'long);
                    output = output + [$string.center(out, 79)];
                case 'core_version:
                    out = "Core: " + $sys.server_info('core_version, 'long);
                    output = output + [$string.center(out, 79)];
            }
        }
        args = args.delete(1);
    }
    return output;
.

public set_motd
    arg what, value;
    
    .perms(sender());
    if (!(what in .parameters()))
        throw(~motd, toliteral(what) + " is not a valid motd parameter, try one of: " + toliteral(.parameters()));
    if (!(type(value) in ['string, 'list]))
        throw(~motd, "Value must be sent as a string or a list of strings.");
    set_var(what, value);
.

public build_html
    arg [args];
    
    return ["<head><title>" + server_name + "</title></head>", "<body>", "<p align=center><img src=\"/tCD.gif\" alt=\"The Cold Dark\"></p>", "<h3 align=center>" + server_title + "</h3>", "<p align=center><tt>", @$code_lib.random_quote(), "</tt></p>", "<p align=center>", @notes, "</p>", "<p align=center>Administrators: " + $sys.admins().map('hname).to_english() + "<br>", "<a href=\"/bin/who\">Currently Connected users</a>: " + tostr($user_db.total_connected()) + "<br>", "Server Lag: " + $lag_watcher.lag() + " seconds.<br>", "Driver: <b><a href=\"http://cold.org:8080/\">ColdX Genesis</a></b> " + $sys.server_info('driver_version), "<br>Core: <b>" + $sys.server_info('core_version, 'long) + "</b>", "<p align=center><a href=\"telnet://" + $sys.server_info('server_hostname) + ":" + tostr($login_daemon.current_port()) + "\"><b><i>Enter the Cold Dark</i></b></a>", "<p align=center>The Cold Dark is a Virtual Environment System.  There is no game in the Cold Dark, the purpose is to create a core which expands the physicality of a Virtual Environment.  To further explore the database, follow the <a href=\"/start.html\">Database Starting Points</a> link.</p>", "<hr>", "<p align=center>", "<a href=\"/bin/project/\"><b>Projects List</b></a> |", "<a href=\"/history.html\"><b>History</b></a> |", "<a href=\"/features.html\"><b>Features</b></a> |", "<a href=\"http://cold.org:8080/Intro/\"><b>Introduction</b></a> |", "<a href=\"/start.html\"><b>DB Starting Points</b></a>", "</p>", @$http_lib.page_tail()];
.

public server_name
    return server_name;
.

public server_title
    return server_title;
.

public connect_help
    return connect_help;
.

public set_connect_help
    arg text;
    
    (> .perms(sender(), 'manager) <);
    connect_help = text;
.



parent $utilities
object $heart

var $root inited 1
var $root objname 'heart
var $root created_on 796268969
var $root flags ['parameters, 'methods, 'code, 'core]
var $heart heart_failures []
var $heart hearts [[$lag_watcher, 813386635, 14]]

public pulse
    var robot, h;
    
    // send every robot that has a heart a pulse, if needed.
    for robot in [1 .. hearts.length()] {
        if (hearts[robot][2] + hearts[robot][3] < time()) {
            hearts = hearts.replace(robot, [hearts[robot][1], time(), hearts[robot][3]]);
            (| hearts[robot][1].pulse() |);
        }
    }
.

public add_heartbeat
    arg [delay];
    var p;
    
    delay = [@delay, 60][1];
    for p in [1 .. hearts.length()] {
        if (hearts[p][1] == sender()) {
            hearts = hearts.replace(p, [sender(), 0, delay]);
            return;
        }
    }
    hearts = [@hearts, [sender(), 0, delay]];
.

public del_heartbeat
    var h, pos;
    
    h = [];
    for pos in [1 .. hearts.length()] {
        if (hearts[pos][1] == sender()) {
            if (pos > 1)
                h = [@s, hearts.subrange(1, pos - 1)];
            if (pos < hearts.length())
                h = [@h, hearts.subrange(pos + 1)];
            hearts = h;
            return;
        }
    }
    throw(~objnf, "Sender (" + tostr(sender()) + ") does not have a heartbeat.");
.



parent $utilities
object $projects

var $root objname 'projects
var $root created_on 799192627
var $root inited 1
var $root flags ['parameters, 'methods, 'code, 'core]
var $projects lists #[['bugs, []], ['suggested, []], ['considered, []], ['accepted, []], ['rejected, []], ['completed, []]]

public add_list
    arg list;
    
    (> .perms(sender()) <);
    if (!lists)
        lists = #[];
    if (type(list) != 'symbol)
        throw(~type, "Lists must be submitted as a symbol.");
    lists = lists.add(list, []);
.

public del_list
    arg list;
    var p;
    
    (> .perms(sender()) <);
    if (!(| lists[list] |))
        throw(~listnf, "List " + toliteral(list) + " is not found in list index.");
    for p in (lists[list])
        p.change_list_to(0);
    lists = lists.del(list);
.

public add_project_to_list
    arg list, project;
    
    (> .perms(sender()) <);
    if (type((| lists[list] |)) == 'error)
        throw(~listnf, "List " + toliteral(list) + " is not found in list  index.");
    if (!project.has_ancestor($project))
        throw(~project, "Invalid project.");
    lists = lists.add_elem(list, project);
    project.change_list_to(list);
.

public del_project_from_list
    arg list, project;
    
    (> .perms(sender()) <);
    if (!(| lists[list] |))
        throw(~listnf, "List " + toliteral(list) + " is not found in list index.");
    if (!project.has_ancestor($project))
        throw(~project, "Invalid project.");
    lists = lists.del_elem(list, project);
    project.change_list_to(list);
.

public list_projects
    var list, project, out, line;
    
    out = ["<head><title>" + $motd.server_name() + " Projects List</title><center><img src=\"/tCD.gif\"><p><i><b><font size=+1>PROJECTS LIST</font></b></i></center></head>", "<body>"];
    for list in (lists.keys()) {
        if (!lists[list])
            continue;
        out = out + ["<hr width=50% align=center><center><font size=+1><b>" + tostr(list).capitalize() + " Projects</b></font></center>", "<pre>", "<i><b>Project                                  Developers</b></i>"];
        for project in (lists[list])
            out = out + ["<a href=\"/bin/project?" + project.objname() + "\"><b>" + project.name() + "</b></a>" + $string.fill(40 - project.name().length()) + " " + project.developers().map('hname).to_english()];
        out = out + ["</pre>"];
    }
    return out + ["</pre>"];
.

public lists
    return lists;
.

public set_project_in_list
    arg list, project;
    var l;
    
    // this removes project from any existing list and inserts to desired list
    (> .perms(sender()) <);
    if (!(| lists[list] |))
        throw(~listnf, "List " + toliteral(list) + " is not found in list index.");
    if (!project.has_ancestor($project))
        throw(~project, "Invalid project.");
    for l in (lists.keys()) {
        if (project in list[l])
            (| .del_project_from_list(l, project) |);
    }
    (> .add_project_to_list(l, project) <);
.



parent $utilities
object $scheduler

var $root objname 'scheduler
var $root created_on 810294797
var $root inited 1
var $root flags ['parameters, 'methods, 'code, 'core]
var $scheduler task_index 1
var $scheduler blocked_tasks #[]
var $scheduler task_queue []
var $scheduler sub_schedulers [$heart]
var $scheduler expected_lag 0
var $scheduler server_lag 0

public sleep
    arg howlong;
    
    .add_task(howlong, 'resume_job, task_id());
    $sys.suspend();
.

public remove_first_task
    var len, i, min;
    
    // sender must be an agent, or admin
    catch any {
        if (caller() != this())
            throw(~perm, "Invalid call to private method");
        len = task_queue.length();
        i = 1;
        while (i != len) {
            min = len;
            if (i * 2 < len && task_queue[i * 2][2] < task_queue[min][2])
                min = i * 2;
            if (i * 2 + 1 < len && task_queue[i * 2 + 1][2] < task_queue[min][2])
                min = i * 2 + 1;
            task_queue = task_queue.replace(i, task_queue[min]);
            i = min;
        }
        task_queue = task_queue.subrange(1, len - 1);
    } with handler {
        // $sys.log(traceback());
    }
    if (!task_queue)
        task_index = 1;
.

public task_queue
    // sender must be system, for now
    (> .perms(sender(), 'system) <);
    return task_queue;
.

public del_task
    arg tid;
    var task;
    
    // sender must be system, for now.
    (> .perms(sender(), 'system) <);
    if (type(tid) != 'integer)
        throw(~type, "Task Identification must be an integer");
    for task in (task_queue) {
        if (task[1] == tid) {
            task_queue = task_queue.delete(task in task_queue);
            return 1;
        }
    }
    throw(~tasknf, "No task found by that TID");
.

public add_task
    arg time, method, [args];
    var task, i, tid, flags, x, y, tmpq, task_time;
    
    // use `@info $scheduler' for more information. 
    // [tid, time, time(), sender(), caller(), method, flags, args]
    // time is seconds from insertion time that it will be run (ie 300 == 5 mins)
    //
    if (type(time) != 'integer || type(method) != 'symbol || type(args) != 'list)
        throw(~type, "Arguments are not an integer, symbol, and list.");
    if (time < 1)
        throw(~time, "Time is negative.");
    if (time > 31536000)
        throw(~time, "Try to schedule a task LESS than a year from now?");
    
    //
    task_index = task_index + 1;
    tid = task_index;
    
    // flags can be set in a system only add_task, for now set them as 'system
    flags = ['system];
    task = [tid, time, time(), sender(), caller(), method, flags, args];
    task_queue = task_queue + [task];
    i = task_queue.length();
    
    //
    //    while (i > 1 && task[2] < task_queue[i / 2][2] + task_queue[i / 2][3]) {
    //        task_queue = task_queue.replace(i, task_queue[i / 2]);
    //        i = i / 2;
    //    }
    task_queue = task_queue.replace(i, task);
    tmpq = [];
    while (task_queue) {
        y = 1;
        task_time = task_queue[1][2] + task_queue[1][3];
        for x in [1 .. task_queue.length()] {
            if (task_time > task_queue[x][2] + task_queue[x][3]) {
                y = x;
                task_time = task_queue[y][2] + task_queue[y][3];
            }
        }
        tmpq = tmpq + [task_queue[y]];
        task_queue = task_queue.delete(y);
    }
    task_queue = tmpq;
    return tid;
.

public pulse
    var task, sub;
    
    // called by $sys.heartbeat
    if (caller() != $sys)
        throw(~perm, "Sender is not system");
    while (task_queue && time() > task_queue[1][2] + task_queue[1][3]) {
        task = task_queue[1];
        (| ._run_task(task) |);
        .remove_first_task();
    }
    
    // call sub schedulers 
    for sub in (sub_schedulers)
        (| sub.pulse() |);
.

public _run_task
    arg task;
    var code, args, a;
    
    // called by $scheduler only
    if (caller() != this())
        throw(~perm, "Caller is not this");
    catch any {
        // setup the args by hand, becuase we use eval and it expects a string
        args = task[8];
        if (args) {
            for a in [1 .. args.length()]
                args = args.replace(a, $data_lib.unparse(args[a]));
            args = $list.to_string(args, ",");
        } else {
            args = "";
        }
        code = "." + tostr(task[6]) + "(" + args + ");";
    
        // run it through eval as the sender():
        // task[5].eval([code], task[4]);
        task[5].as_this_run(task[5], task[6], task[8]);
    } with handler {
        // bounce the errors to the person, or to the system board if it's 'system
        //catch any {
        (| task[4].tell(["SCHEDULER ERROR: ", @traceback()]) |);
    
        //  } with handler {
        //  if ('system in task[7]) {
        // $channels.announce('System, traceback()[1]);
        // $channels.announce('System, "task: " + $data_lib.unparse(task));
        // }
        //  }
    }
.

public sys_add_task
    arg time, method, sender, caller, flags, [args];
    var task, i, tid, x, y, tmpq, task_time;
    
    // use `@info $scheduler' for more information. 
    // [tid, time, time(), sender(), caller(), method, flags, args]
    //
    if (!$sys.is_agent(sender()))
        throw(~perm, "Sender is not an agent or admin, use .add_task()");
    if (type(time) != 'integer || type(method) != 'symbol || type(args) != 'list)
        throw(~type, "Arguments are not an integer, symbol, and list.");
    if (time < 1)
        throw(~time, "Time is negative.");
    if (time > 31536000)
        throw(~time, "Try to schedule a task LESS than a year from now?");
    if (!valid(sender))
        throw(~type, "The argument for sender is not a valid object");
    if (!valid(caller))
        throw(~type, "The argument for caller is not a valid object");
    if (type(flags) != 'list)
        throw(~type, "Send flags as a list of symbols");
    
    //
    task_index = task_index + 1;
    tid = task_index;
    
    // flags can be set in a system only add_task, for now set them as 'system
    task = [tid, time, time(), sender, caller, method, flags, args];
    task_queue = task_queue + [task];
    
    //
    i = task_queue.length();
    while (i > 1 && task[2] < task_queue[i / 2][2] + task_queue[i / 2][3]) {
        task_queue = task_queue.replace(i, task_queue[i / 2]);
        i = i / 2;
    }
    task_queue = task_queue.replace(i, task);
    tmpq = [];
    while (task_queue) {
        y = 1;
        task_time = task_queue[1][2] + task_queue[1][3];
        for x in [1 .. task_queue.length()] {
            if (task_time > task_queue[x][2] + task_queue[x][3]) {
                y = x;
                task_time = task_queue[y][2] + task_queue[y][3];
            }
        }
        tmpq = tmpq + [task_queue[y]];
        task_queue = task_queue.delete(y);
    }
    task_queue = tmpq;
    return tid;
.

public add_sub_scheduler
    arg object;
    
    if (!$sys.is_admin(sender()))
        throw(~perm, "Only admins may add sub schedulers.");
    if (type(object) != 'dbref)
        throw(~type, "Object must be a dbref.");
    sub_schedulers = [@sub_schedulers, object];
.

public del_sub_scheduler
    arg object;
    var pos, s;
    
    if (!$sys.is_admin(sender()))
        throw(~perm, "Only admins may delete sub schedulers.");
    if (type(object) != 'dbref)
        throw(~type, "Object must be a dbref.");
    pos = object in sub_schedulers;
    if (!pos)
        throw(~objnf, "Object not a sub scheduler.");
    s = [];
    if (pos > 1)
        s = [@s, sub_schedulers.subrange(1, pos - 1)];
    if (s < sub_schedulers.length())
        s = [@s, sub_schedulers.subrange(pos + 1)];
    sub_schedulers = s;
.

public has_blocked_tasks
    arg ident;
    
    return blocked_tasks.contains(ident);
.

public block_task
    arg ident;
    var tasks;
    
    // I want to be atomic!
    // Add the task_id to the queue of blocked tasks for this identifier.
    if (blocked_tasks.contains(ident))
        tasks = blocked_tasks[ident] + [task_id()];
    else
        tasks = [task_id()];
    blocked_tasks = blocked_tasks.add(ident, tasks);
    
    // And go to sleep until we are woken.
    $sys.suspend();
.

public unblock_task
    arg ident;
    var tasks;
    
    // I want to be atomic!
    // The caller should have checked first, but we will fail silently.
    if (!.has_blocked_tasks(ident))
        return;
    
    // Get the blocked tasks queue for this identifier.
    tasks = blocked_tasks[ident];
    
    // If this is the last blocked task, then clear the queue, otherwise
    // just delete the task_id that we are resuming.
    if (tasks.length() == 1)
        blocked_tasks = blocked_tasks.del(ident);
    else
        blocked_tasks = blocked_tasks.add(ident, tasks.delete(1));
    
    // Wake it up and go.
    $sys.resume(tasks[1]);
.

protected resume_job
    arg tid;
    
    $sys.resume(tid);
.



parent $utilities
object $housekeeper

var $root inited 1
var $root objname 'housekeeper
var $root created_on 796268969
var $root flags ['parameters, 'methods, 'code, 'core]
var $messaged verbs #[]

public did_disconnect
    var task_queue, task;
    
    if (caller() != $user)
        throw(~perm, "Permission denied");
    
    // because of guests
    if (valid(sender()))
        $scheduler.add_task(300, 'move_user_home, sender());
.

public _move_user_home
    arg who;
    var home, curloc;
    
    .perms(caller(), 'this);
    if (who.connected())
        return;
    curloc = who.location();
    home = who.home();
    if (curloc == home)
        return;
    curloc.sending_user_home(who);
    who.move_to(home);
.

public move_user_home
    arg who;
    var home, curloc;
    
        if (who.connected())
            return;
        curloc = who.location();
        home = who.home();
        if (curloc == home)
            return;
        who.move_to(home);
        curloc.did_housekeep(who);
.



parent $frob
object $logic_frob

var $root child_index 6
var $root trusted []
var $root inited 1
var $root objname 'logic_frob
var $root created_on 796268969
var $root flags ['parameters, 'methods, 'code, 'core]

public test
    arg [args];
    
    return 0;
.



parent $frob
parent $named
parent $world
object $realms_frob

var $root inited 1
var $root objname 'realms_frob
var $root created_on 796268969
var $root flags ['parameters, 'methods, 'code, 'core]
var $named name ['uniq, "realms_frob", "the realms_frob"]
var $named name_aliases []
var $realms_frob local #[]

public new
    arg [args];
    
    args = [@args, ['interior]][1];
    (> $places.is_place(sender()) <);
    local = local.add(sender(), args);
    return (<this(), args>);
.

public destroyed
    arg frob;
    
.

public init_realms_frob
    local = #[];
.

root uninit_realms_frob
    var x;
    
    for x in (local)
        x[1].set_realm($realm_of_creation, 'interior);
.

public place_destroyed
    arg place;
    var x;
    
    local = local.del(place);
    for x in (local.keys())
        (| x.place_destroyed(place) |);
.

public local
    return local;
.

public name
    arg [article];
    
    if (article && type(article[1]) == 'frob)
        article = article.delete(1);
    return (> pass(@article) <);
.

public realm_name
    arg [args];
    var realms;
    
    realms = .realms();
    while (realms.length() > 2)
        realms = realms.delete(2);
    return realms.to_string(", ");
.

public realms
    arg [args];
    var r;
    
    if (definer() == this())
        return [];
    return [.name()] + (.parents())[1].realms();
.



parent $frob
object $ctext_frob

var $root inited 1
var $root objname 'ctext_frob
var $root created_on 796268969
var $root flags ['parameters, 'methods, 'code, 'core]

public _ctext
    arg this;
    
    return this;
.

public new
    arg data, [evaluator];
    
    evaluator = [@evaluator, $compile_evaluator][1];
    if (type(data) == 'frob && class(data) == this())
        return data;
    if (type(data) == 'string)
        data = (evaluator.compile_cml(data))['result];
    return (<this(), data>);
.

public eval_ctext
    arg this, [vars];
    var keys;
    
    vars = [@vars, #[]][1];
    keys = vars.keys();
    if (!('time in keys))
        vars = vars.add('time, 'pre);
    if (!('evaluator in keys))
        vars = vars.add('evaluator, $base_evaluator);
    if (!('this in keys))
        vars = vars.add('this, sender());
    if (!('sender in keys))
        vars = vars.add('sender, sender());
    return vars['evaluator].eval_ctext(vars, this);
.

public finish
    arg this;
    var term, output, line;
    
    output = [];
    line = "";
    for term in (this) {
        if (type(term) in ['symbol, 'integer, 'string]) {
            line = line + tostr(term);
        } else if (type(term) == 'dbref) {
            line = line + term.objname();
        } else if (type(term) == 'list) {
            if (term[1] == 'b_stmt) {
                output = [@output, line];
                line = "";
            } else {
                output = [@output, line, term.to_string()];
                line = "";
            }
        } else {
            line = line + $data_lib.unparse(term);
        }
    }
    output = [@output, line];
    return output;
.

public uncompile
    arg this;
    
    return $uncompile_evaluator.eval_ctext(this);
.

public ctext
    arg this;
    
    return this;
.

public append
    arg this, data, [evaluator];
    var tmp;
    
    // Make sure data is ctext
    evaluator = [@evaluator, $compile_evaluator][1];
    data = $ctext_frob.new(data, evaluator);
    if (this[1][1] == 'text_stmt && data[1][1] == 'text_stmt) {
        //If this is a text_stmt we just add the data to the end
        this = ['text_stmt, [@this[1][2], @(data.ctext())[2]]];
    } else {
        // anything else get wrapped in a text_stmt
        this = ['text_stmt, @this[1], @data.ctext()];
    }
    return (<this(), this>);
.



parent $frob
object $ctext_tag

var $root objname 'ctext_tag
var $root child_index 2
var $root created_on 806278619
var $root inited 1
var $root flags ['parameters, 'methods, 'code, 'core]

public new
    arg name, flags, args, method;
    var item, self, eflags, uflags;
    
    self = [name, #[], #[], []];
    eflags = #[];
    uflags = #[];
    for item in (flags) {
        switch (type(item)) {
            case 'string:
                eflags = eflags.add(item, 1);
            case 'list:
                if (type(item[1]) != 'string)
                    throw(~flagerr, "Flag name must be a string.");
                if (type(item[2]) == 'list && item[2].length() == 1)
                    item = item.insert(2, item[2][1]);
                if (type(item[2]) == 'frob && class(item[2]) == $ctext_generator)
                    uflags = uflags.add(item[1], item[2]);
                else
                    eflags = eflags.add(item[1], item[2]);
            default:
                throw(~flagerr, "Flag must be a string or key,value pair.");
        }
    }
    return (<this(), [name, eflags, uflags, args, method]>);
.

public name
    arg self;
    
    // if (type(self)=='dictionary)
    // return self['name];
    //else
    return self[1];
.

public uflags
    arg self;
    
    //if (type(self)=='dictionary)
    // return (| self['uflags]|) || #[];
    //else
    return self[3];
.

public add_flags
    arg self, key, value;
    var efalgs, uflags;
    
    eflags = (| self['flags] |) || #[];
    uflags = (| self['uflags] |) || #[];
    if (type(value) == 'frob && class(value) == $ctext_generator) {
        uflags = uflags.add(key, value);
    } else {
        eflags = eflags.add(key, value);
        if (key in uflags.keys())
            uflags = uflags.del(key);
    }
    if (eflags.keys())
        self = self.add('eflags, eflags);
    if (uflags.keys())
        self = self.add('uflags, uflags);
    return (<this(), self>);
.

public args
    arg self;
    
    //if (type(self)=='dictionary)
    // return (| self['args]|) || [];
    //else
    return self[4];
.

public set_args
    arg self, args;
    
    //if (type(self) == 'dictionary)
    // return <this(), self.add('args,args)>;
    //else
    return (<this(), self.replace(4, args)>);
.

public append_arg
    arg self, new;
    var args;
    
    //if (type(self)=='dictionary) {
    //  args = (| self['args] |) || [];
    //args = [@args,new];
    //  return <this(), self.add('args,args)>;
    //} else {
    args = self[4];
    args = [@args, new];
    return (<this(), self.replace(4, args)>);
    
    //}
.

public method
    arg self;
    
    return self[5];
.

public ctext_flags
    arg self;
    
    //if (type(self)=='dictionary)
    // return ((| self['flags]|) || #[]).union((|self['uflags]|) || #[]);
    //else
    return self[2].union(self[3]);
.

public ctext_uflags
    arg self;
    
    //if (type(self)=='dictionary)
    // return (| self['uflags]|) || #[];
    //else
    return self[3];
.

public add_ctext_flag
    arg self, key, value;
    var eflags, uflags;
    
    //if (type(self) == 'dictionary) {
    //  eflags = (|self['flags]|) || #[];
    //  uflags =  (|self['uflags]|) || #[];
    //} else {
    eflags = self[2];
    uflags = self[3];
    
    //}
    if (type(value) == 'frob && class(value) == $ctext_generator) {
        uflags = uflags.add(key, value);
    } else {
        eflags = eflags.add(key, value);
        if (key in uflags.keys())
            uflags = uflags.del(key);
    }
    
    //if (type(self)=='dictionary) {
    // if (eflags.keys())
    //   self = self.add('flags,eflags);
    // if (uflags.keys())
    //   self = self.add('uflags,uflags);
    //} else {
    self = self.replace(2, eflags);
    self = self.replace(3, uflags);
    
    //}
    return (<this(), self>);
.



parent $frob
parent $thing
object $thing_frob

var $root child_index 1
var $root fertile 1
var $root inited 1
var $root objname 'thing_frob
var $root created_on 796268969
var $root flags ['parameters, 'methods, 'code, 'fertile, 'core]
var $located location $nowhere
var $located obvious 1
var $described prose []
var $messaged verbs #[]
var $gendered gender $gender_neuter
var $named name ['uniq, "thing_class", "the thing_class"]
var $named name_aliases []

public name
    arg [args];
    
    if (args && type(args[1]) == 'dictionary)
        return (> ._name(@args) <);
    return (> pass(@args) <);
.

public namef
    arg [args];
    
    if (args && type(args[1]) == 'dictionary)
        return (> ._namef(@args) <);
    return (> pass(@args) <);
.

public short_description
    arg [args];
    
    if (args && type(args[1]) == 'dictionary)
        return (> ._description(@args) <);
    return (> pass(@args) <);
.

public long_description
    arg [args];
    
    if (args && type(args[1]) == 'dictionary)
        return (> ._description(@args) <);
    return (> pass(@args) <);
.

public prose
    arg [args];
    var dict;
    
    if (args && type(args[1]) == 'dictionary)
        return (> ._prose(@args) <);
    return (> pass(@args) <);
.

public _namef
    arg dict, [args];
    
    return (> ._name(dict, args) <);
.

public _prose
    arg dict, [args];
    
    (> .perms(sender(), 'this) <);
    return (| dict['prose] |) || "You see nothing special.";
.

public _name
    arg dict, [args];
    var name, article;
    
    name = (| dict['name] |) || [[.name(), 'normal], [.name_aliases()]];
    switch (name[1][2]) {
        case 'prop:
            return name[1][1];
        case 'uniq:
            return "the " + name[1][1];
        case 'normal:
            return name[1][1].a_or_an() + " " + name[1][1];
    }
.

public _description
    arg dict, [args];
    
    (> .perms(sender(), 'this) <);
    return [._name(dict), @._prose(dict)];
.

public match_name
    arg [args];
    
    if (args && type(args[1]) == 'dictionary)
        return (> ._match_name(@args) <);
    return (> pass(@args) <);
.

public _match_name_exact
    arg this, str;
    
    return $string.match_begin(this['name][1][1], str);
.

public _match_name_aliases
    arg this, str;
    
    return (| str in this['name][2] |);
.

public _match_name
    arg this, str;
    
    return ._match_name_exact(this, str) || ._match_name_aliases(this, str);
.



parent $frob
object $read_parser

var $root inited 1
var $root objname 'read_parser
var $root created_on 796268969
var $root flags ['parameters, 'methods, 'code, 'core]

public parse_line
    arg dict, line;
    var cmd;
    
    if (!line) {
        // we have to do this as such, because of logic (shrug)
        dict = dict.add('status, 'not_done);
        return dict.add_elem('lines, line);
    } else if (line[1] == ".") {
        if (line.length() > 1 && line[2] == ".")
            line = line.subrange(2);
        else if (line.length() == 1)
            return dict.add('status, 'done);
    } else if (line[1] == ">") {
        if (line.length() > 1 && line[2] == ">") {
            line = line.subrange(2);
        } else {
            dict = dict.add('command, line.subrange(2));
            return dict.add('status, 'pass_command);
        }
    } else if (line == "@abort") {
        return dict.add('status, 'abort);
    }
    dict = dict.add('status, 'not_done);
    return dict.add_elem('lines, line);
.

public new
    arg task_id, count;
    
    return (<this(), #[['lines, []], ['status, 'not_done], ['count, count], ['task_id, task_id]]>);
.

public parse
    arg dict, line;
    var line, result;
    
    // checks the incoming line to see if its a keeper, or a command.
    if (dict['count] == 'one) {
        dict = dict.add_elem('lines, line);
        return (<this(), dict.add('status, 'done)>);
    } else {
        return (<this(), .parse_line(dict, line)>);
    }
.

public status
    arg dict;
    
    return dict['status];
.

public task_id
    arg dict;
    
    return dict['task_id];
.

public lines
    arg dict;
    
    return dict['lines];
.

public command
    arg dict;
    
    return (| dict['command] |) || "";
.

public add
    arg dict, [args];
    
    return (<this(), (> dict.add(@args) <)>);
.



parent $frob
object $ctext2_frob

var $root objname 'ctext2_frob
var $root created_on 807320067
var $root inited 1
var $root flags ['parameters, 'methods, 'code, 'core]

public new
    arg data, [vars];
    var ret;
    
    vars = [@vars, #[]][1];
    return (<this(), [data, vars]>);
.

public eval_ctext
    arg this;
    var data, vars;
    
    data = this[1];
    vars = this[2];
    vars = vars.add('time, 'pre);
    if (!('sender in vars.keys()))
        vars = vars.add('sender, sender());
    if (!('evalautor in vars.keys()))
        vars = vars.add('evaluator, $cml2_base_eval);
    return vars['evaluator].eval_ctext(data, vars);
.

public format
    arg this;
    var data, vars;
    
    data = this[1];
    vars = this[2];
    vars = vars.add('time, 'format);
    if (!('receiver in vars.keys()))
        vars = vars.add('receiver, sender());
    if (!('formatter in vars.keys()))
        vars = vars.add('formatter, $cml2_telnet_format);
    if (!('evalautor in vars.keys()))
        vars = vars.add('evaluator, $cml2_base_eval);
    this = vars['formatter].eval_ctext(data, vars);
    return $buffer.from_string(this);
.

public set_var
    arg this, name, value;
    var vars;
    
    vars = this[2];
    vars = vars.add(name, value);
    return (<this(), [this[1], vars]>);
.

public get_var
    arg this, name;
    
    return this[2][name];
.

public vars
    arg this;
    
    return this[2];
.

public _ctext
    arg this;
    
    return this[1];
.

public set_vars
    arg this, new;
    var vars, key;
    
    vars = this[2];
    for key in (new.keys())
        vars = vars.add(key, new[key]);
    return (<this(), [this[1], vars]>);
.

public uncompile
    arg this;
    var vars;
    
    vars = this[2];
    if (!('uncompiler in vars.keys()))
        vars = vars.add('uncompiler, $cml2_uncompiler);
    return vars['uncompiler].eval_ctext(this[1]);
.

public append
    arg this, new, [br];
    var data, tmp;
    
    data = this[1];
    if (br) {
        switch (br[1]) {
            case "p":
                data = [@data, (<$ctext_generator, #[['name, "p"]]>)];
            case "br":
                data = [@data, (<$ctext_generator, #[['name, "br"]]>)];
            default:
                data = [@data, $ctext_generator.new(br[1], [], [])];
        }
    }
    if (type(new) == 'list)
        data = [@data, @new];
    else if (type(new) == 'frob && class(new) == $ctext2_frob)
        data = [@data, @new._ctext()];
    else
        data = [@data, new];
    return (<this(), [data, this[2]]>);
.



parent $frob
object $message_frob

var $root inited 1
var $root objname 'message_frob
var $root created_on 796268969
var $root flags ['parameters, 'methods, 'code, 'core]

public add_entry
    arg this, key, what;
    var data;
    
    this = this.add(key, what);
    return (<this(), this>);
.

public del_entry
    arg this, key;
    
    this = this.del(key);
    return (<this(), this>);
.

public eval_ctext
    arg this, vars;
    var key, new, temp, t, list, vars;
    
    new = .new(#[]);
    vars = vars.add('this, sender());
    if ("general" in this.keys())
        vars = vars.add("$general", "general");
    list = this.keys();
    for key in (list) {
        temp = this[key].set_var('this, vars['this]).eval_ctext();
        new = new.add_entry((| vars["$" + key] |) || vars[key], temp);
    }
    return new;
.

public new
    arg what;
    var key, this;
    
    if (type(what) != 'dictionary)
        throw(~type, "A message should be a dictionary.");
    this = #[];
    for key in (what)
        this = this.add_entry(key[1], key[2]);
    return (<this(), this>);
.

public has_entry
    arg this, name;
    
    return name in this.keys();
.

public get_part
    arg this, part;
    
    return this[part];
.

public uncompile
    arg this;
    var key, output;
    
    output = #[];
    for key in (this)
        output = output.add(key[1], key[2].uncompile());
    return output;
.

public parts
    arg this;
    
    return this.keys();
.

public message
    arg name, [definer];
    var a, message, mes, m, empty;
    
    //retrieve the specified message as ctext
    if (definer)
        definer = definer[1];
    else
        definer = (._find_message_definer(name))[2];
    message = $message_frob.new(#[], #[]);
    empty = $ctext_frob.new("");
    for a in (.ancestors()) {
        catch ~methodnf, ~messagenf {
            return a.local_message(name, definer);
        }
    }
    throw(~messagenf, "No matching message.");
.

public set_vars
    arg this, new;
    var d;
    
    for d in (this.keys())
        this = this.add(d, this[d].set_vars(new));
    return (<this(), this>);
.

public format
    arg this, vars;
    var new;
    
    new = (| this[vars['receiver]] |) || (| this["general"] |) || $ctext_frob.new([""]);
    new = new.set_vars(vars);
    new = new.format();
    return new;
.



parent $command_cache
object $editors

var $root trusted []
var $root inited 1
var $root objname 'editors
var $root created_on 796268969
var $root flags ['parameters, 'methods, 'code, 'core]
var $has_commands shortcuts #[]
var $command_cache shortcut_cache 0
var $command_cache remote_cache 0
var $command_cache local_cache 0



parent $user
object $builder

var $root owners [$builder]
var $root inited 1
var $root owned [$builder]
var $root manager $builder
var $root quota 75000
var $root objname 'builder
var $root created_on 796268969
var $root flags ['parameters, 'methods, 'code, 'core]
var $root managed [$builder]
var $messaged verbs #[]
var $location contents []
var $located location $body_cave
var $located obvious 1
var $user password "*"
var $user connected_at 0
var $user last_command_at 0
var $user connections []
var $user modes #[]
var $user prompt ""
var $user parsers [$command_parser]
var $user tell_traceback ['brief, 0]
var $user context #[]
var $user filters []
var $command_aliases command_aliases []
var $mail_list letters #[]
var $mail_list letters_index #[]
var $mail_list senders 1
var $mail_list readers []
var $mail_list notify [$builder]
var $mail_list last_letter 0
var $mail_list mail []
var $mail_ui subscribed #[[$builder, 791485891]]
var $mail_ui current #[['location, 0], ['list, $builder]]
var $gendered gender $gender_neuter
var $described prose []
var $named name ['prop, "Generic Builder", "Generic Builder"]
var $named name_aliases []
var $user_data user_data #[['real_name, [1, "???"]], ['email, [1, "???"]]]
var $has_commands local #[["@par?ents", [["@par?ents", "*", "@par?ents <any>", 'parents_cmd, #[[1, ['any, []]]]]]], ["@dest?roy", [["@dest?roy", "*", "@dest?roy <any>", 'destroy_cmd, #[[1, ['any, []]]]]]], ["@child?ren|@kids", [["@child?ren|@kids", "*", "@child?ren|@kids <any>", 'children_cmd, #[[1, ['any, []]]]]]], ["@build", [["@build", "*", "@build <any>", 'build_cmd, #[[1, ['any, []]]]]]], ["@attach", [["@attach", "* to *", "@attach <any> to <any>", 'attach_cmd, #[[1, ['any, []]], [3, ['any, []]]]]]], ["@realm?s", [["@realm?s", "", "@realm?s", 'realms_cmd, #[]]]]]
var $help_ui indices [$help_index_root]
var $help_ui history [$help_summary]
var $help_ui current 1
var $command_cache shortcut_cache 0
var $command_cache remote_cache 0
var $command_cache local_cache 0

protected children_cmd
    arg cmdstr, cmd, what;
    var out;
    
    what = .match_env_nice(what);
    out = ["Children of " + what.namef('xref) + ":"];
    what = what.children();
    return out + $code_lib.generate_object_listing(what, 'parents);
.

protected parents_cmd
    arg cmdstr, cmd, what;
    var out;
    
    what = .match_env_nice(what);
    out = ["Parent" + (.parents().length() > 1 ? "s" | "") + " of " + what.namef('ref) + ":"];
    what = what.parents();
    return out + $code_lib.generate_object_listing(what, 'children);
.

public destroy_cmd
    arg cmdstr, cmd, what;
    var name;
    
    if (sender() != this())
        throw(~perm, "Sender not this.");
    what = .match_env_nice(what);
    catch any {
        name = what.namef('xref);
        what.destroy();
        .tell("Sucessfully destroyed " + name + ".");
    } with handler {
        .tell(traceback()[1][2]);
    }
.

public _build_query_coordinates
    arg [returning];
    var radial, azimuth, coord;
    
    (> .perms(sender(), 'this) <);
    ._build_hint(5);
    while (1) {
        coord = .prompt("Exit coordinates (radial,azimuth): ");
        if (coord == "@abort")
            throw(~abort, "Abort");
        if (coord == "@shortcuts") {
            ._build_shortcuts();
            continue;
        }
        if (!coord) {
            .tell("Invalid Coordinates.");
            continue;
        }
        catch ~coordnf, ~invcoord {
            if (coord.is_numeric()) {
                coord = coord.explode_english_list();
                if (coord.length() != 2) {
                    .tell("Seperate coordinates with a comma.");
                    continue;
                }
                if (!coord[1].is_numeric() || !coord[2].is_numeric()) {
                    .tell("Invalid coordinates.");
                    continue;
                }
                if (!coord[1].is_numeric() || !coord[2].is_numeric()) {
                    .tell("Invalid coordinates.");
                    continue;
                }
                radial = toint(coord[1]);
                azimuth = toint(coord[2]);
            } else {
                coord = $places.coordinates(coord);
                radial = coord[1];
                azimuth = coord[2];
            }
            (> $places.valid_coordinates(radial, azimuth) <);
        } with handler {
            .tell(traceback()[1][2]);
            continue;
        }
        return [radial, azimuth];
    }
.

public build_cmd
    arg cmdstr, cmd, str;
    var dest, source, exits;
    
    (> .perms(sender(), 'this) <);
    source = .location();
    if (!(| source.will_attach('source) |))
        return "This room is not publicly extendable.";
    
    // Establish the objects
    catch any {
        dest = (> ._build_get_location(str) <);
        if (!(| dest[1].will_attach('dest) |)) {
            .tell(dest[1].name() + " will not allow you to attach to it!");
            ._build_cleanup(dest);
            return "** Build Aborted **";
        }
        exits = (> ._build_get_exits(dest, source) <);
        (> ._build_attach_exit(source, dest[1], @exits[1]) <);
        (> ._build_attach_exit(dest[1], source, @exits[2]) <);
    } with handler {
        if (dest)
            ._build_cleanup(dest);
        .tell_traceback(traceback());
        return [traceback()[1][2], "** Build Aborted **"];
    }
    
    // Flesh them out
    if (dest[2])
        (| dest.configure() |);
    if (exits[1])
        (| exits[1][1].configure() |);
    if (exits[2])
        (| exits[2][2].configure() |);
    return ["Completed building (*whew*)", "---"];
.

public _build_shortcuts
    var x;
    
    (> .perms(sender(), 'this) <);
    .tell("Radial/Azimuth Coordinate shortcuts:");
    .tell(" Shortcut            Radial Azimuth");
    for x in ($places.coordinate_shortcuts())
        .tell(" " + x[1].pad(20) + tostr(x[2][1]).pad(7) + tostr(x[2][2]));
.

public _build_hint
    arg n;
    
    (> .perms(sender()) <);
    if ((| .setting("experienced") |) == "yes")
        return;
    .tell($places.build_hint(n));
.

public _build_cleanup
    arg [what];
    var obj;
    
    (> .perms(caller(), definer()) <);
    for obj in (what) {
        if (valid(obj[1]) && obj[2]) {
            .tell("Destroying " + obj[1].name() + "...");
            (| obj[1].destroy() |);
        }
    }
.

public _build_query_exit
    arg source, dest;
    var exit, line, name;
    
    (> .perms(sender(), 'this) <);
    line = "Exit from " + dest.name() + " to " + source.name() + ":";
    name = (> ._build_loop_name_query(line) <);
    if (name in ["none", "<none>"])
        return 0;
    exit = (| $exit.spawn() |);
    if (!exit) {
        .tell("Unable to create exit!");
        return 0;
    }
    ._build_set_name(exit, @name);
    return exit;
.

public attach_cmd
    arg cmdstr, cmd, source_str, prep, dest_str;
    var source, dest, coords, coords_str, exit, ename;
    
    (> .perms(sender(), 'this) <);
    if (!source_str)
        source = .location();
    else
        source = .match_env_nice(source_str);
    dest = .match_env_nice(dest_str);
    exit = ._build_query_exitname(dest, source);
    if (exit == -1)
        return .tell("Aborted.");
    coords = ._build_query_coordinates();
    if (!coords)
        return .tell("Aborted.");
    ._build_query_prose(exit);
    catch any {
        exit.attach(source, dest, @coords);
    } with handler {
        .tell("Ack, unable to attach exit because:");
        .tell("  " + traceback()[1][2]);
        (| exit.destroy() |);
        return;
    }
    .tell("Successfully attached exit.");
.

public realms_cmd
    arg cmdstr, cmd;
    var x, realms;
    
    (> .perms(sender(), 'this) <);
    realms = $places.known_realms().union($realms_frob.descendants());
    .tell("Realms: " + realms.map('name).to_english());
.

public _build_set_name
    arg obj, name, aliases;
    var a, x;
    
    (> .perms(sender(), 'this) <);
    catch any {
        obj.set_name(@name);
    } with handler {
        .tell("Unable to set name; " + traceback()[1][2]);
        .tell("Setting name as " + tostr(obj.objname('symbol)));
        obj.set_name(tostr(obj.objname('symbol)));
    }
    for a in (aliases)
        obj.add_name_alias(a);
.

public _build_loop_name_query
    arg prompt, [args];
    var invalid, syntax, line, out;
    
    (> .perms(sender(), 'this) <);
    invalid = [@args, "Invalid name."][1];
    syntax = [@args, "", ""][2];
    ._build_hint(3);
    while (1) {
        line = .prompt(prompt);
        if (line == "@abort")
            throw(~abort, "Aborted");
        if (!line) {
            .tell(invalid);
            continue;
        }
        line = (| $code_lib.parse_name(line) |);
        if (!line) {
            .tell("Empty name.");
            continue;
        }
        return line;
    }
.

public _build_get_location
    arg name;
    var dest;
    
    (> .perms(sender()) <);
    ._build_hint(1);
    if (name) {
        if ($string.match_begin(name, "to "))
            name = name.subrange(4);
        name = (| $code_lib.parse_name(name) |);
    }
    if (!name)
        name = (> ._build_loop_name_query("Enter destination name: ") <);
    
    // first try to see if it already exists
    catch any {
        dest = (> .match_environment(name[1][1]) <);
        (> $places.is_place(dest) <);
        return [dest, 0];
    } with handler {
        return [(> ._build_create_place(name) <), 1];
    }
.

public _build_query_realm
    arg there;
    var line, realm, r;
    
    while (!realm) {
        line = .prompt("What realm is " + there.name() + " in? ");
        if (line == "@abort")
            throw(~abort, "Aborted");
        if (line == "@realms" || line == "?") {
            .tell("Known realms:");
            for r in ($realms_frob.descendants())
                .tell("  " + r.name());
            continue;
        }
        realm = $places.match_realm(line);
    }
    
    // add interior/exterior query later
    return realm;
.

public _build_create_place
    arg name;
    var name, dest, realm;
    
    (> .perms(sender()) <);
    ._build_hint(2);
    dest = (> $places.place('default_new).spawn() <);
    ._build_set_name(dest, @name);
    ._build_hint(4);
    catch any {
        realm = (> ._build_query_realm(dest) <);
    } with handler {
        ._build_cleanup([dest, 1]);
        rethrow(error());
    }
    
    // because of how settings work, we want to give it the string name instead
    dest.set_setting($place, "realm", realm.name());
    return dest;
.

public _build_attach_exit
    arg source, dest, exit, coords;
    var line;
    
    (> .perms(sender()) <);
    catch any {
        exit.attach(source, dest, @coords);
    } with handler {
        .tell("Unable to attach " + exit.name() + " because: ");
        .tell("  " + traceback()[1][2]);
        line = .prompt("Continue building? ");
        if (line in ["no", "n"])
            throw(~abort, "Aborted");
    }
.

public _build_get_exits
    arg dest, source;
    var eleave, earrive;
    
    // Get the exits
    catch any {
        if (eleave = (> ._build_query_exit(dest[1], source) <)) {
            eleave = [eleave, (> ._build_query_coordinates() <)];
            if (earrive = (> ._build_query_exit(source, dest[1]) <))
                earrive = [earrive, (> $places.invert_coordinates(@eleave[2]) <)];
        }
    } with handler {
        if (eleave) {
            if (earrive)
                ._build_cleanup([eleave[1], 1], [earrive[1], 1]);
            else
                ._build_cleanup([eleave[1], 1]);
        }
        rethrow(error());
    }
    return [eleave, earrive];
.



parent $user
object $guest

var $root child_index 44
var $root owners [$guest]
var $root inited 1
var $root owned [$guest]
var $root manager $guest
var $root quota 75000
var $root objname 'guest
var $root created_on 796268969
var $root flags ['parameters, 'methods, 'code, 'core]
var $root managed [$guest]
var $messaged verbs #[]
var $location contents []
var $located location $body_cave
var $located obvious 1
var $user password "*"
var $user connected_at 0
var $user last_command_at 0
var $user connections []
var $user modes #[]
var $user prompt ""
var $user parsers [$command_parser]
var $user tell_traceback ['verbose, 4]
var $user context #[]
var $user filters []
var $command_aliases command_aliases []
var $mail_list letters #[]
var $mail_list letters_index #[]
var $mail_list senders 1
var $mail_list readers 1
var $mail_list notify [$guest]
var $mail_list last_letter 0
var $mail_list mail []
var $mail_ui subscribed #[[$guest, 791485891]]
var $mail_ui current #[['location, 0], ['list, $guest]]
var $gendered gender $gender_neuter
var $described prose []
var $named name ['prop, "Generic Guest Object", "Generic Guest Object"]
var $named name_aliases []
var $user_data user_data #[['real_name, [1, "???"]], ['email, [1, "???"]]]
var $help_ui indices [$help_index_root]
var $help_ui history [$help_summary]
var $help_ui current 1
var $command_cache shortcut_cache 0
var $command_cache remote_cache 0
var $command_cache local_cache 0

root init_guest
    .set_title("a guest");
.

public logout
    arg connection;
    
    (| pass(connection) |);
    .destroy();
.

public title_cmd
    arg cmdstr, com, str;
    
    (> .perms(sender(), 'this) <);
    .tell("Guests are not allowed to change their titles.");
.



parent $user
object $reaper

var $root quota 75000
var $root inited 1
var $root objname 'reaper
var $root created_on 796268969
var $root flags ['core]
var $messaged verbs #[]
var $user_data user_data #[['real_name, [1, "???"]], ['email, [1, "???"]]]
var $command_aliases command_aliases []
var $named name ['prop, "Reaper", "Reaper"]
var $named name_aliases []
var $gendered gender $gender_neuter
var $described prose []
var $location contents []
var $located location $body_cave
var $located obvious 1
var $body body_parts #[]
var $user password "*"
var $user connected_at 0
var $user last_command_at 0
var $user connections []
var $user creation_time 780375877
var $user parsers [$command_parser]
var $user filters []
var $user action ""
var $user prompt ""
var $user context #[]
var $help_ui indices [$help_index_root]
var $help_ui history [$help_summary]
var $help_ui current 1
var $mail_list mail []
var $mail_list senders 1
var $mail_list notify [$reaper]
var $mail_ui subscribed #[[$reaper, 791485891]]
var $mail_ui current #[['location, 0], ['list, $reaper]]
var $command_cache shortcut_cache 0
var $command_cache remote_cache 0
var $command_cache local_cache 0



parent $user
object $no_one

var $root inited 1
var $root manager $no_one
var $root quota 75000
var $root objname 'no_one
var $root created_on 796268969
var $root flags ['parameters, 'methods, 'code, 'core]
var $root managed [$no_one]
var $command_aliases command_aliases []
var $messaged verbs #[]
var $location contents []
var $located location $body_cave
var $located obvious 1
var $user password "*"
var $user connected_at 0
var $user last_command_at 0
var $user connections []
var $user creation_time 759878010
var $user action ""
var $user modes #[]
var $user prompt ""
var $user parsers [$command_parser]
var $user tell_traceback ['brief, 0]
var $user context #[]
var $user filters []
var $mail_list letters #[]
var $mail_list letters_index #[]
var $mail_list senders 1
var $mail_list notify [$no_one]
var $mail_list last_letter 0
var $mail_list mail []
var $mail_ui subscribed #[[$no_one, 791485891]]
var $mail_ui current #[['location, 0], ['list, $no_one]]
var $gendered gender $gender_neuter
var $described prose []
var $named name ['prop, "No One", "No One"]
var $named name_aliases ["No", "One"]
var $user_data user_data #[['real_name, [1, "???"]], ['email, [1, "???"]]]
var $help_ui indices [$help_index_root]
var $help_ui history [$help_summary]
var $help_ui current 1
var $command_cache shortcut_cache 0
var $command_cache remote_cache 0
var $command_cache local_cache 0



parent $user
object $player

var $root objname 'player
var $root child_index 1
var $root fertile 1
var $root created_on 809926794
var $root inited 1
var $root quota 75000
var $root flags ['core]
var $user password "*"
var $user connected_at 0
var $user last_command_at 0
var $user connections []
var $user parsers [$command_parser]
var $user action ""
var $user context #[]
var $located location $nowhere
var $located obvious 1
var $has_commands shortcuts #[]
var $has_commands remote #[]
var $has_commands local #[["@sheet|@score", [["@sheet|@score", "*", "@sheet|@score <any>", 'sheet_cmd, #[[1, ['any, []]]]]]]]
var $player strength [0, 0]
var $player agility [0, 0]
var $player appearance [0, 0]
var $player health [0, 0]
var $player life [0, 0]
var $player intellect [0, 0]
var $player knowledge [0, 0]
var $player backbone [0, 0]
var $player charisma [0, 0]
var $player humanity [0, 0]
var $player perception [0, 0]
var $player presence [0, 0]
var $player source 0
var $player dead 0
var $player weapons 0
var $player identity 0
var $player affiliation 0
var $player points 0
var $player deaths 0
var $player encumbrance 0
var $player fatigue 0
var $player characteristics 0
var $named name ['proper, "player", "player"]
var $named name_aliases []
var $has_settings defined_settings #[]
var $has_settings local_settings #[]
var $has_settings settings #[]
var $location contents []
var $described prose []
var $gendered gender $gender_neuter
var $mail_ui current #[['location, 0], ['list, $player]]
var $mail_ui subscribed #[[$player, 813278553], [$mail_list_news, 813278553]]
var $mail_list mail []
var $mail_list senders 1
var $mail_list readers [$player]
var $mail_list notify [$player]
var $command_aliases command_aliases []
var $user_data user_data #[['real_name, [1, "???"]], ['email, [1, "???"]]]
var $help_ui history [$help_summary]
var $help_ui indices [$help_index_root]
var $help_ui current 1

protected lower_attribute
    arg attribute, difference;
    var new;
    
    new = get_var(attribute)[1] - difference;
    set_var(attribute, get_var(attribute).replace(1, new));
.

public attribute: disallow_overrides, synchronized
    arg attribute;
    
    if (caller() != definer())
        (> .perms(sender(), $storyteller) <);
    return get_var(attribute);
.

root init_player: disallow_overrides, synchronized
    strength = [0, 0];
    agility = [0, 0];
    appearance = [0, 0];
    health = [0, 0];
    life = [0, 0];
    intellect = [0, 0];
    knowledge = [0, 0];
    backbone = [0, 0];
    charisma = [0, 0];
    humanity = [0, 0];
    perception = [0, 0];
    presence = [0, 0];
    source = [0, 0];
    weapons = [];
.

protected raise_attribute
    arg attribute, difference;
    var new;
    
    new = get_var(attribute)[1] + difference;
    if (new > get_var(attribute)[2])
        new = get_var(attribute)[2];
    set_var(attribute, get_var(attribute).replace(1, new));
    
.

public dead
    return dead;
.

public sheet_cmd
    arg cmdstr, cmd, args;
    var t, out, g;
    
    if (args && !.is($storyteller))
        return "Only storytellers can see other players stats.";
    t = args ? .match_env_nice(args) | this();
    g = $game_lib;
    out = ["Strength:   " + g.format_rank(@t.attribute('strength)) + "Reaction: " + tostr((t.attribute('agility))[1] / 5).pad(8) + "Strength Damage:    0", "Agility:    " + g.format_rank(@t.attribute('agility)) + "Source:   " + g.format_rank(@t.attribute('source)), "Appearance: " + g.format_rank(@t.attribute('appearance)) + "Learn:    " + tostr((t.attribute('intellect))[1] / 3).pad(8) + "Competancy Level:   0", "Intellect:  " + g.format_rank(@t.attribute('intellect)) + "                  Advancement Points: " + tostr(t.attribute('points)), "Knowledge:  " + g.format_rank(@t.attribute('intellect)) + "Life:     " + g.format_rank(@t.attribute('life)), "Presence:   " + g.format_rank(@t.attribute('presence)) + "Health:   " + g.format_rank(@t.attribute('health)) + "Humanity: " + g.format_rank(@t.attribute('humanity)), "Backbone:   " + g.format_rank(@t.attribute('backbone)), "Charisma:   " + g.format_rank(@t.attribute('charisma)) + "Deaths:   " + tostr(t.attribute('deaths)).pad(8) + "Encumbrance: " + tostr(t.attribute('encumbrance)), "Perception: " + g.format_rank(@t.attribute('perception)) + "                  Fatigue:     " + tostr(t.attribute('fatigue))];
    return out;
.

public set_attribute
    arg attribute, value;
    
    (> .perms(sender()) <);
    set_var(attribute, [value, value]);
.



parent $thing
object $nothing

var $root inited 1
var $root objname 'nothing
var $root created_on 796268969
var $root flags ['parameters, 'methods, 'code, 'core]
var $messaged verbs #[]
var $located location 0
var $located obvious 1
var $gendered gender $gender_neuter
var $described prose []
var $named name ['prop, "nothing whatsoever", "nothing whatsoever"]
var $named name_aliases []



parent $place
object $nowhere

var $root inited 1
var $root objname 'nowhere
var $root created_on 796268969
var $root flags ['parameters, 'methods, 'code, 'core]
var $messaged verbs #[]
var $location contents [$located_location, $body, $user, $reaper_log, $log, $http_log, $note, $login_log, $thing_frob, $player]
var $gendered gender $gender_neuter
var $described prose []
var $named name ['prop, "Nowhere", "Nowhere"]
var $named name_aliases []
var $place exits []
var $place realm <$realm_of_creation, ['interior]>
var $place coordinates #[]
var $public public []
var $command_cache shortcut_cache 0
var $command_cache remote_cache 0
var $command_cache local_cache 0



parent $place
object $body_cave

var $root objname 'body_cave
var $root created_on 808865147
var $root inited 1
var $root flags ['parameters, 'methods, 'code, 'core]
var $has_settings defined_settings #[]
var $has_settings local_settings ["public-home"]
var $has_settings settings #[[$place, #[["public-home", "1"]]]]
var $has_commands shortcuts #[]
var $has_commands remote #[]
var $has_commands local #[]
var $command_cache shortcut_cache 0
var $command_cache remote_cache 0
var $command_cache local_cache 0
var $named name ['normal, "the Body Cave", "a the Body Cave"]
var $named name_aliases []
var $gendered gender $gender_neuter
var $messaged verbs #[]
var $described prose []
var $location contents [$reaper, $builder, $guest, $no_one, $programmer, $admin, $player]
var $place exits []
var $place realm <$realm_of_creation, ['interior]>
var $place coordinates #[]
var $physical visibility -100



parent $place
object $void

var $root inited 1
var $root objname 'void
var $root created_on 796268969
var $root flags ['parameters, 'methods, 'code, 'core]
var $messaged verbs #[]
var $location contents [$thing, $lost_and_found]
var $gendered gender $gender_neuter
var $described prose ["A place existing for the soul purpose of doing so, when it wishes be."]
var $named name ['uniq, "Void", "the Void"]
var $named name_aliases []
var $place exits []
var $place realm <$realm_of_creation, ['interior]>
var $place coordinates #[]
var $public public []
var $command_cache shortcut_cache 0
var $command_cache remote_cache 0
var $command_cache local_cache 0



parent $place
object $detailed_place

var $root objname 'detailed_place
var $root created_on 809582204
var $root inited 1
var $root flags ['parameters, 'methods, 'code, 'core]
var $has_commands shortcuts #[]
var $has_commands remote #[]
var $has_commands local #[]
var $command_cache shortcut_cache 0
var $command_cache remote_cache 0
var $command_cache local_cache 0
var $has_settings defined_settings #[]
var $has_settings local_settings #[]
var $has_settings settings #[]
var $named name ['uniq, "place_128", "the place_128"]
var $named name_aliases []
var $gendered gender $gender_neuter
var $messaged verbs #[]
var $described prose []
var $location contents []
var $place exits []
var $place realm <$realm_of_creation, ['interior]>
var $place coordinates #[]



parent $place
object $the_pit

var $root inited 1
var $root objname 'the_pit
var $root created_on 796268969
var $root flags ['parameters, 'methods, 'code, 'core]
var $has_settings local_settings ["realm"]
var $messaged verbs #[]
var $location contents []
var $gendered gender $gender_neuter
var $described prose <$ctext2_frob, [["This is a room with a low ceiling, worn couches and furniture, and walls worn from scribbling, burn marks, and other unrecognizeable usages. Desipite its cramped feel, there is a warm comfortable atmosphere, almost as if somebody could just come here and relax for a while. To the north is a cement opening in the wall."], #[['line, 1], ['this, $the_pit]]]>
var $named name ['prop, "The Pit", "The Pit"]
var $named name_aliases []
var $place exits []
var $place realm $realm_of_creation
var $place coordinates #[]
var $public public []
var $command_cache shortcut_cache 0
var $command_cache remote_cache #[["l", [["l?ook"], [$described]]], ["lo", [["l?ook"], [$described]]], ["loo", [["l?ook"], [$described]]], ["look", [["l?ook"], [$described]]], ["@lock", [["@lock"], [$thing]]], ["@unlock", [["@unlock"], [$thing]]], ["@boot", [["@boot"], [$thing]]]]
var $command_cache local_cache 0



parent $connection
object $login_connection

var $root objname 'login_connection
var $root child_index 1722
var $root writes [$login_connection_1720, $login_connection_1721, $login_connection_1722]
var $root created_on 809051865
var $root inited 1
var $root indestructable 1
var $root quota_exempt -1
var $root writers [$login_daemon]
var $root flags ['parameters, 'methods, 'code, 'core]
var $root owned [$login_connection_1720, $login_connection_1721, $login_connection_1722]
var $connection interface $login_interface
var $connection active 0
var $connection buffer `[]
var $connection host ""
var $connection daemon 0
var $connection line_buffer []
var $connection timeout 0
var $connection read_block 0
var $connection started_at 0



parent $connection
object $http_connection

var $root objname 'http_connection
var $root child_index 1854
var $root owned [$http_connection_1854]
var $root created_on 809075134
var $root inited 1
var $root indestructable 1
var $root quota_exempt -1
var $root flags ['parameters, 'methods, 'code, 'core]
var $root writes [$http_connection_1854]
var $connection buffer `[27]
var $connection host ""
var $connection daemon 0
var $connection active 0
var $connection line_buffer []
var $connection interface $http_interface
var $connection timeout 0
var $connection read_block 0
var $connection started_at 0

public address
    return (> pass() <);
.



parent $daemon
object $http_daemon

var $root objname 'http_daemon
var $root writes [$http_connection_1854]
var $root created_on 809075222
var $root inited 1
var $root indestructable 1
var $root flags ['parameters, 'methods, 'code, 'core]
var $daemon default_port 1180
var $daemon connection $http_connection
var $daemon next_connection $http_connection_1854
var $daemon current_port 1180



parent $daemon
object $login_daemon

var $root objname 'login_daemon
var $root writes [$login_connection_1720, $login_connection_1721, $login_connection_1722]
var $root created_on 809051992
var $root inited 1
var $root indestructable 1
var $root flags ['parameters, 'methods, 'code, 'core]
var $daemon connection $login_connection
var $daemon default_port 1138
var $daemon current_port 1138
var $daemon next_connection $login_connection_1722



parent $connection_interface
object $login_interface

var $root objname 'login_interface
var $root child_index 620
var $root owned [$login_interface_620]
var $root writes [$login_connection_1720, $login_connection_1721, $login_connection_1722]
var $root created_on 809051864
var $root inited 1
var $root quota_exempt -1
var $root indestructable 1
var $root flags ['parameters, 'methods, 'code, 'core]
var $has_commands local #[["@who", [["@who", "", "@who", 'who_cmd, #[]]]], ["m?otd", [["m?otd", "", "m?otd", 'motd_cmd, #[]]]], ["h?elp", [["h?elp", "", "h?elp", 'help_cmd, #[]]]], ["guest|connect-guest", [["guest|connect-guest", "*", "guest|connect-guest <any>", 'connect_guest_cmd, #[[1, ['any, []]]]]]], ["c?onnect", [["c?onnect", "*", "c?onnect <any>", 'connect_cmd, #[[1, ['any, []]]]]]], ["@quit|QUIT", [["@quit|QUIT", "", "@quit|QUIT", 'quit_cmd, #[]]]], ["create", [["create", "*", "create <any>", 'create_cmd, #[[1, ['any, []]]]]]]]
var $command_cache shortcut_cache []
var $command_cache remote_cache 0
var $command_cache local_cache #[["@who", ["@who"]], ["m", ["m?otd"]], ["mo", ["m?otd"]], ["mot", ["m?otd"]], ["motd", ["m?otd"]], ["h", ["h?elp"]], ["he", ["h?elp"]], ["hel", ["h?elp"]], ["help", ["h?elp"]], ["guest", ["guest|connect-guest"]], ["connect-guest", ["guest|connect-guest"]], ["c", ["c?onnect"]], ["co", ["c?onnect"]], ["con", ["c?onnect"]], ["conn", ["c?onnect"]], ["conne", ["c?onnect"]], ["connec", ["c?onnect"]], ["connect", ["c?onnect"]], ["@quit", ["@quit|QUIT"]], ["QUIT", ["@quit|QUIT"]], ["create", ["create"]]]


protected connect_cmd
    arg cmdstr, cmd, args;
    var syn, stderr, passwd, name, user;
    
    syn = cmd + " <name> <password>";
    stderr = "Either that user does not exist or has a different password.";
    args = args.explode();
    if (args.length() < 2)
        .tell_error("Last word is taken as the password.", syn);
    passwd = args[args.length()];
    name = args.subrange(1, args.length() - 1).to_string();
    user = (| $user_db.find(name) |) || .tell_error(syn, stderr);
    user.check_password(passwd) || .tell_error(syn, stderr);
    .connection().change_interface(user);
.

protected create_cmd
    arg cmdstr, cmd, args;
    var syn, msg, user, semail;
    
    syn = cmd + " <name> <password> <email@host>";
    semail = $sys.get_system_email('login);
    args = args.explode();
    args.length() == 3 || .tell_error(syn);
    catch any {
        user = $sys.create_user(@args);
    } with handler {
        if (user)
            (| user.destroy() |);
        if (error() == ~duplicate) {
            msg = "The user " + toliteral(args[1]) + " already exists.";
        } else {
            msg = ["There was a problem creating you:"];
            msg = [@msg, traceback()[1][2]];
            msg = [@msg, "If there is a problem contact: " + semail];
        }
        (| $login_log.log(traceback()) |);
        (> .tell_error(syn, msg) <);
    }
    .connection().change_interface(user);
.

protected quit_cmd
    arg cmdstr, cmd;
    
    .print("Goodbye.");
    return 'disconnect;
.

protected connect_guest_cmd
    arg cmdstr, cmd, args;
    var syn, msg, c, email, name, result, semail, user;
    
    syn = cmd + " <your name> <your email>";
    semail = $sys.get_system_email('login);
    args = args.explode();
    if (args.length() < 2)
        (> .tell_error(syn) <);
    name = args.subrange(1, args.length() - 1).to_string();
    email = args[args.length()];
    if ($sys.validate_email_addresses())
        result = $code_lib.valid_email(email);
    if (result[1] != 'valid) {
        switch (result[1]) {
            case 'invalid:
                (| .tell_error(c, syn) |);
                c.write("The given email address is not a legitimate address.");
                c.write("Specify both username and hostname.");
                return;
            case 'invip, 'invhostname:
                c.write("Your hostname seems to be invalid.");
                c.write("You should set a valid email address.");
        }
    }
    catch any {
        user = (> $sys.create_user(name, 0, email, 'anonymous_user_class) <);
    } with handler {
        if (error() == ~duplicate) {
            msg = ["The name " + toliteral(words[1]) + " is already in use."];
        } else {
            msg = "There was a problem creating you:";
            msg = [msg, " => " + traceback()[1][2]];
            msg = [@msg, "If there is a problem contact: " + semail];
        }
        (| $login_log.log(traceback()) |);
        (> .tell_error(syn, msg) <);
    }
    .connection().change_interface(user);
.

protected who_cmd
    arg cmdstr, cmd;
    
    .print($code_lib.generate_listing($user_db.connected()));
.

protected help_cmd
    arg cmdstr, cmd;
    
    .print($motd.connect_help());
.

protected null_cmd
    arg [args];
    
    return (> .invalid_cmd(@args) <);
.

protected invalid_cmd
    arg [args];
    var line;
    
    (> .perms(sender(), 'this) <);
    line = $login_interface.local_cache().to_list().slice(2).slice(1).compress();
    .print("Try: " + line.to_english("", " or "));
.

protected print
    arg what;
    
    .connection().write(what);
.

protected motd
    var out;
    
    out = $motd.build('default) + ["", " ** Use 'H?elp' for a list of commands **".center(79), ""];
    return out;
.

protected motd_cmd
    arg cmdstr, cmd;
    
    .print(.motd());
.

protected tell_error
    arg syntax, [problem];
    var problem, line, sprefix, prefix, length;
    
    length = 79;
    if (syntax)
        .print("=> Syntax: `" + syntax + "`");
    if (problem) {
        for line in (problem) {
            if (type(line) == 'string)
                line = line.wrap_lines(length, "!  ", 1);
            .print(line);
        }
    }
    .print("!  ** use h?elp for a list of commands and their usages **");
    throw(~stop, "", 'no_traceback);
.

public connection_starting
    arg host, port;
    
    (> .perms(caller(), $connection) <);
    .print(.motd());
.

public parse_line
    arg line;
    var cmd, c, match, parsed, i, m, a, u;
    
    catch any {
        while (line && line[1] == " ")
            line = line.subrange(2);
        if (!line) {
            return .null_cmd(line);
        } else {
            cmd = line.explode();
            cmd = [line, cmd[1], cmd.subrange(2).to_string() || ""];
            c = (| $login_interface.match_in_local_cache(@cmd) |);
            if (c && c[1] == 'local) {
                // screw duplicates, take the first match
                match = c[2][1];
                m = match[2];
                i = match[5];
                parsed = i.keys();
                for a in [1 .. m.length()] {
                    if (a in parsed)
                        m = m.replace(a + 2, (> $command_parser.convert_arg(i[a][1], m[a + 2], $no_one, i[a][2] ? i[a][2][1] | $no_one, $no_one) <));
                }
                return (> .(match[4])(@m) <);
            }
            return (> .invalid_cmd(line) <);
        }
    } with handler {
        if (traceback()[1][3] != 'no_traceback) {
            .print($parse_lib.traceback(traceback()));
            return 'disconnect;
        }
    }
.

public daemon_shutdown
    var i;
    
    (> .perms(caller(), $daemon) <);
    for i in (.children())
        (| i.destroy() |);
.



parent $connection_interface
object $http_interface

var $root objname 'http_interface
var $root child_index 1843
var $root writes [$http_connection_1854]
var $root created_on 809075102
var $root inited 1
var $root indestructable 1
var $root quota_exempt -1
var $root managed [$http_interface_1843]
var $root flags ['parameters, 'methods, 'code, 'core]
var $root owned [$http_interface_1843]
var $has_commands shortcuts #[]
var $has_commands remote #[]
var $has_commands local #[]
var $command_cache shortcut_cache []
var $command_cache remote_cache 0
var $command_cache local_cache 0
var $http_interface cache []
var $http_interface method "GET"
var $http_interface http "HTTP/0.9"
var $http_interface status 405
var $http_interface URI "/"
var $http_interface bytes 205
var $http_interface ctype 0
var $http_interface connection 0
var $http_interface cached 0
var $http_interface full 0
var $http_interface header 0

public new
    arg c;
    var i;
    
    (> .perms(caller(), $connection) <);
    i = .pull_from_cache();
    i.set_connection(c);
    return i;
.

public pull_from_cache
    var i;
    
    if (sender() != this())
        throw(~perm, "Sender is not this.");
    return .spawn();
    while (cache) {
        i = cache[1];
        cache = cache.delete(1);
        if (valid(i))
            break;
    }
    if (i) {
        i.set_cached(1);
        return i;
    } else {
        return .spawn();
    }
.

public add_obj_to_cache
    arg i;
    
    if (caller() != definer())
        throw(~perm, "Sender is not this.");
    i.destroy();
    return;
    if (!cache)
        cache = [];
    cache = cache.setadd(i);
    i.set_cached(1);
.

public del_obj_from_cache
    arg i;
    
    if (sender() != this())
        throw(~perm, "Sender is not this.");
    if (!cache)
        return;
    cache = cache.setremove(i);
    i.set_cached(0);
.

root init_http_interface
    .initialize_http();
.

public close
    var c;
    
    if (connection) {
        .log_request(connection.address());
        c = connection;
        connection = 0;
        c.close();
        definer().add_obj_to_cache(this());
    }
.

public connection_starting
    arg addr, port;
    
.

public connection_going_away
    arg addr, port;
    
    (| .close() |);
    .initialize_http();
    definer().add_obj_to_cache(this());
.

public initialize_http
    if (caller() != definer())
        throw(~perm, "Caller is not " + definer().objname());
    method = "";
    http = "HTTP/1.0";
    status = 200;
    URI = "";
    bytes = 0;
    ctype = $http_lib.html_version();
    full = 1;
    header = #[];
.

public send_header
    arg [lines];
    
    if (!full)
        return;
    connection.write([http + " " + tostr(status), "Server: ColdWeb-db/0.2", "Content-type: " + ctype, "Content-length: " + tostr(bytes)] + lines + [""]);
.

public respond
    arg body;
    
    if (type(body) != 'buffer)
        body = $buffer.from_strings(body);
    bytes = body.length();
    .send_header();
    connection.write(body);
    .close();
.

public respond_with_file
    arg fstat;
    
    bytes = fstat[2];
    .send_header();
    connection.echo_file("html" + URI);
    .close();
.

public find_filename
    var file, actual, stat, ifiles, i;
    
    stat = (| stat_file("html" + URI) |);
    if (!stat) {
        ifiles = ["home.html", "index.html", "welcome.html"];
        file = URI;
        actual = URI;
        if (file[file.length()] != "/")
            file = file + "/";
        while (!stat && ifiles) {
            URI = file + ifiles[1];
            stat = (| stat_file("html" + URI) |);
            ifiles = ifiles.delete(1);
        }
        if (!stat) {
            URI = actual;
            status = 404;
            .respond($http_lib.response(status, "Unable to find URL " + toliteral(URI) + "."));
            return 0;
        }
    }
    if (i = URI.rindex(".")) {
        switch (URI.subrange(i + 1)) {
            case "txt":
                ctype = "text/plain";
            case "gif":
                ctype = "image/gif";
        }
    }
    return stat;
.

public process_header
    arg line, i;
    var n, v;
    
    if (!method) {
        status = 400;
        return .respond($http_lib.response(400, "No Method Specified!"));
    }
    n = line.subrange(1, i - 1);
    if (line[i + 1] == " ")
        i = i + 1;
    v = line.subrange(i + 1);
    header = header.add_elem(n, v);
.

public process_method
    arg line;
    
    line = line.explode();
    if (!(line[1] in $http_lib.http_methods())) {
        status = 405;
        return .respond($http_lib.response(status, "Method: \"" + line[1] + "\"."));
    }
    method = line[1];
    line = line.delete(1);
    if (!line) {
        status = 400;
        return .respond($http_lib.response(status, "No URI specified!"));
    }
    URI = line[1];
    if (line.length() > 1) {
        http = line[2];
    } else {
        http = "HTTP/0.9";
        full = 0;
    }
.

public handle_method
    if (!method) {
        status = 400;
        return .respond($http_lib.response(status, "No Method specified!"));
    }
    .(tosym("http_method_" + method))();
.

public parse_line
    arg line;
    var i;
    
    if (i = ":" in line) {
        .process_header(line, i);
    } else if (line) {
        .process_method(line);
        if (!full) {
            if (method != "GET") {
                status = 400;
                .respond($http_lib.response(status, "Invalid HTTP/0.9 method."));
            } else {
                .handle_method();
            }
        }
    } else {
        .handle_method();
    }
.

public log_request
    arg host;
    var line;
    
    line = host + " - - [" + $time.format("%d %h %y %H:%M") + "] \"" + method + " " + URI + " " + http + "\" " + tostr(status) + " " + tostr(bytes);
    if ((| header["User-Agent"] |))
        line = line + ";; " + header["User-Agent"].to_string();
    $sys.log(line);
.

public http_method_GET
    var info, target, gate;
    
    if (URI == "/") {
        .respond($motd.build_html());
    } else if ($string.match_begin(URI, "/~")) {
        target = (| $user_db.find((URI.explode("/"))[1].subrange(2)) |);
        info = (| target.setting("home-page") |);
        if (info) {
            status = 302;
            .redirect(info);
        } else {
            status = 404;
            .respond($http_lib.response(404, "Unable to find user " + (URI.explode("/"))[1].subrange(2)));
        }
    } else if ($string.match_begin(URI, "/bin")) {
        target = URI.explode("/").delete(1);
        if (!target) {
            status = 300;
            return .respond($http_lib.response(300, ["Multiple Choices: "] + $http_lib.list_gateways()));
        }
        gate = target[1];
        target = target.delete(1);
        if ("?" in gate) {
            target = [gate.subrange(("?" in gate) + 1), @target];
            gate = gate.subrange(1, ("?" in gate) - 1);
        }
        if (!(gate in $http_lib.gateways().keys())) {
            status = 502;
            .respond($http_lib.response(502, "Bad gateway: \"" + gate + "\""));
        } else if (!gate) {
            status = 300;
            return .respond($http_lib.response(300, ["Multiple Choices: "] + $http_lib.list_gateways()));
        } else {
            catch any {
                info = (> $http_lib.(tosym("bin_" + gate))(@target) <);
                status = info[1];
                if (type(info[2]) == 'buffer || status != 200)
                    return .respond(info[2]);
                return .respond(info[2] + $http_lib.page_tail());
            } with handler {
                status = 500;
                return .respond($http_lib.html_traceback(500, traceback()));
            }
        }
    } else if (info = .find_filename()) {
        .respond_with_file(info);
    }
.

public http_method_HEAD
    var stat;
    
    if (!stat = .find_filename())
        return;
    bytes = stat[2];
    .send_header();
    .respond_with_file(stat);
    .close();
.

public set_connection
    arg c;
    
    (> .perms(caller(), definer()) <);
    connection = c;
.

public set_cached
    arg v;
    
    if (caller() != definer())
        throw(~perm, "Caller is not " + definer().objname() + ".");
    cached = v;
.

public redirect
    arg location;
    var body;
    
    body = $buffer.from_strings($http_lib.response(302, "Relocated at: " + location));
    bytes = body.length();
    .send_header("Location: " + location);
    connection.write(body);
    .close();
.



parent $help_index
object $help_index_root

var $root objname 'help_index_root
var $root created_on 806626103
var $root inited 1
var $root flags ['parameters, 'methods, 'code, 'core]
var $named name ['prop, "Index", "Index"]
var $named name_aliases []
var $help_index topics []



parent $help_node
object $help_summary

var $root inited 1
var $root objname 'help_summary
var $root created_on 796268969
var $root flags ['parameters, 'methods, 'code, 'core]
var $named name ['prop, "Summary", "Summary"]
var $named name_aliases []
var $help_node linked_by []
var $help_node links #[]
var $help_node body <$ctext2_frob, [["This help system is still being re-engineered, for now access the help topics via the world wide web at:", " ", <$ctext_format, ["p", #[], #[], [], 'do_p]>, "http://www.cold.org:8080/Software/ColdCore/help/"], #[['line, 2], ['this, $help_summary]]]>



parent $note
object $log

var $root child_index 4
var $root fertile 1
var $root trusted []
var $root inited 1
var $root objname 'log
var $root created_on 796268969
var $root flags ['methods, 'code, 'fertile, 'core]
var $messaged verbs #[["read * on %this", ['read_cmd, 'remote]]]
var $described prose ["the place that Ye administrators should be logging somewhat impacting changes that others would like to know about."]
var $gendered gender $gender_neuter
var $located location $nowhere
var $located obvious 1
var $named name ['uniq, "Generic Log", "the Generic Log"]
var $named name_aliases ["log", "changes", "log"]
var $text text ["9-28-94/22:33:17> foo"]
var $public public ['readable]
var $has_commands remote #[["read", [["read", "* on *", "read <any> on <this>", 'read_cmd, #[[1, ['any, []]], [3, ['this, []]]]]]]]

public read_cmd
    arg [args];
    var loglen, text;
    
    if (0) {
        // later on i'll adjust this so you can 'read from line 12 on log'
        return;
    } else {
        text = .text();
        loglen = text.length();
        sender().tell(["---", .namef() + ", entries " + tostr(loglen - 10) + " to " + tostr(loglen) + " (last 10 lines).", "---"]);
        sender().tell(text.subrange(loglen - 10));
        sender().tell("---");
    }
.

public log
    arg line;
    var l;
    
    (> .perms(caller(), 'trusts) <);
    if (type(line) == 'list) {
        for l in (line)
            .ins_line($time.format("%d %h %y %H:%M") + "> " + l);
    } else {
        .ins_line($time.format("%d %h %y %H:%M") + "> " + line);
    }
.



parent $event_frob
object $movement_event

var $root inited 1
var $root objname 'movement_event
var $root created_on 796268969
var $root flags ['parameters, 'methods, 'code, 'core]
var $event_frob type 'movement

public exit
    arg event;
    
    return event['exit];
.

public new
    arg actor, source, dest;
    
    //  (> .perms(caller(), $exit) <);
    return (<this(), #[['exit, sender()], ['actor, actor], ['source, source], ['dest, dest]]>);
.

public dest
    arg frob;
    
    return frob['dest];
.

public source
    arg args;
    
    return args[3].source();
.

public dispatch
    arg dict;
    
    dict['source].announce_event((<this(), dict>));
    dict['dest].announce_event((<this(), dict>));
.

public process
    arg dict;
    
.

public actor
    arg frob;
    
    return frob['actor];
.



parent $time_root
object $time

var $root inited 1
var $root objname 'time
var $root created_on 796268969
var $root flags ['parameters, 'methods, 'code, 'core]
var $time_root secs_per_min 60
var $time_root secs_per_hour 3600
var $time_root secs_per_day 86400
var $time_root secs_per_week 604800
var $time_root secs_per_year 31536000
var $time_root created_on 0
var $time_root mins_per_hour 60
var $time_root hours_per_day 24
var $time_root days_per_year 365
var $time_root year_begin 0
var $time_root days ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]
var $time_root months #[["Jan", ["January", 1, 31]], ["Feb", ["February", 2, 28]], ["Mar", ["March", 3, 31]], ["Apr", ["April", 4, 30]], ["May", ["May", 5, 31]], ["Jun", ["June", 6, 30]], ["Jul", ["July", 7, 31]], ["Aug", ["August", 8, 31]], ["Sep", ["September", 9, 30]], ["Oct", ["October", 10, 31]], ["Nov", ["November", 11, 30]], ["Dec", ["December", 12, 31]]]
var $time_root standard 1

public format
    arg [args];
    
    return (> strftime(@args) <);
.

public elapsed
    arg time, [flag];
    var str, t, p;
    
    // compares args[1] with time() and returns hh:mm elapsed
    // will eventually make flags do things like 'long etc.  For now its
    // just your own time, rather than time().
    flag = [@flag, 'stopwatch][1];
    str = "";
    switch (flag) {
        case 'long:
            return .to_english(time);
        default:
            if (time > 356400)
                p = 3;
            else
                p = 2;
            str = str + tostr(time / 3600).pad(-p, "0");
            str = str + ":" + tostr(time % 3600 / 60).pad(-2, "0");
            return str;
    }
.

public dhms
    arg secs, [long];
    var ret_str, x;
    
    if (long)
        long = 1;
    if (secs > 86400) {
        x = secs / 86400;
        ret_str = tostr(x) + (long ? " day" + (x < 2 ? "" | "s") | "d");
    } else if (secs > 3600) {
        x = secs / 3600;
        ret_str = tostr(x) + (long ? " hr" + (x < 2 ? "" | "s") | "h");
    } else if (secs > 60) {
        x = secs / 60;
        ret_str = tostr(x) + (long ? " min" + (x < 2 ? "" | "s") | "m");
    } else {
        ret_str = tostr(secs) + (long ? " sec" + (secs < 2 ? "" | "s") | "s");
    }
    return ret_str;
.

public month_str
    arg [args];
    var month, m, time, option;
    
    // args[1] = time (opt); args[2] == 'num, 'long, 'short;
    if (args && type(args[1]) != 'integer) {
        time = time();
        option = [@args, 'long][1];
    } else {
        time = [@args, time()][1];
        option = [@args, 'long, 'long][2];
    }
    if (type(time) == 'integer)
        month = ctime(time).subrange(5, 3);
    else
        month = time.subrange(5, 3);
    
    // special case:
    switch (option) {
        case 'short:
            return month;
        case 'num:
            return tostr((.months())[month][2]);
        default:
            return (.months())[month][1];
    }
.

public year_str
    arg [time];
    var how;
    
    // (time, how) time defaults to time(), how defaults to long
    how = [@time, 'long, 'long][2];
    time = [@time, .time()][1];
    if (how == 'long)
        return ctime(time).subrange(21);
    return ctime(time).subrange(23);
.

public ldate
    arg [args];
    var time, ctime, how, options, sep, hrs, mins, secs;
    
    // takes a bunch of numbers and returns info depending upon what the
    // symbol is.  Time is optional, so is how for that matter.
    // options are listed to the right of the descriptions.
    // How:     'long     -- Monday, January 10th, 1994 (default)
    //          'noyear   -- 'long without the year.
    //          'short    -- Mon, Jan 10, 94
    //          'date     -- 01-Dec-95 (ie: DD-MMM-YY)
    //          'dmy      -- 10/01/94 (ie: DD/MM/YY) ['slash | 'dash]
    //          'mdy      -- 01/10/94 (ie: MM/DD/YY) ['slash | 'dash]
    //          'ymd      -- 94/10/01 (ie: YY/MM/DD) ['slash | 'dash]
    args = [@args, 'long];
    if (type(args[1]) != 'integer) {
        time = time();
        how = args[1];
        args = args.delete(1);
    } else {
        time = args[1];
        how = args[2];
        args = args.subrange(3);
    }
    
    // figure options first
    options = [@args, 0][1];
    sep = "/";
    if (options) {
        switch (options) {
            case 'dash:
                sep = "-";
            case 'slash:
                sep = "/";
        }
    }
    
    // figure actual time
    switch (how) {
        case 'long:
            return .day_str(time) + ", " + .month_str(time) + " " + .month_day_str(time, 'num) + ", " + .year_str(time);
        case 'noyear:
            return .day_str(time) + ", " + .month_str(time) + " " + .month_day_str(time, 'num);
        case 'short:
            ctime = ctime(time);
            return ctime.pad(3) + "," + ctime.subrange(4).pad(7) + ", `" + .year_str(time, 'short);
        case 'date:
            return .month_day_str(time) + "-" + .month_str(time, 'short) + "-" + .year_str(time, 'short);
        case 'dmy:
            return .month_day_str(time) + sep + .month_str(time, 'num) + sep + .year_str(time, 'short);
        case 'mdy:
            return .month_str(time, 'num) + sep + .month_day_str(time) + sep + .year_str(time, 'short);
        case 'ymd:
            return .year_str(time, 'short) + sep + .month_str(time, 'num) + sep + .month_day_str(time, 'num);
    }
.

public ltime
    arg [args];
    var time, ctime, how, options, sep1, sep2, ampm, hrs, mins, secs;
    
    // takes a bunch of numbers and returns info depending upon what the symbol is
    // time is optional, so is how for that matter.  Uses '12hr '_ampm for default
    // options are listed after the descriptions.
    //          '24hr     -- 24:00 ['a_m_p_m|'ampm|'_ampm|'ap|'no_ampm]
    //          '12hr     -- 12:00 ['a_m_p_m|'ampm|'_ampm|'ap|'no_ampm]
    //          '24hr_sec -- 24:00:00 ['a_m_p_m|'ampm|'_ampm|'ap|'no_ampm]
    //          '12hr_sec -- 12:00:00 ['a_m_p_m|'ampm|'_ampm|'ap|'no_ampm]
    //          'long     -- twelve thirty four pm['a_m_p_m|'ampm|'_ampm|'no_ampm]
    //          'hour     -- Twelve o'clock
    // BTW, incase your wondering, ltime stands for Lynx Time; i'm a bastard 8b
    args = [@args, '12hr];
    if (type(args[1]) != 'integer) {
        time = time();
        how = args[1];
        args = args.delete(1);
    } else {
        time = args[1];
        how = args[2];
        args = args.subrange(3);
    }
    
    // figure options first
    options = [@args, '_ampm][1];
    sep1 = "/";
    sep2 = ":";
    ampm = ["", ""];
    if (options) {
        switch (options) {
            case 'dash:
                sep1 = "-";
            case 'slash:
                sep1 = "/";
            case 'a_m_p_m:
                ampm = [" a.m.", " p.m."];
            case 'ampm:
                ampm = ["am", "pm"];
            case '_ampm:
                ampm = [" am", " pm"];
            case 'ap:
                ampm = ["a", "p"];
            case 'no_ampm:
                ampm = ["", ""];
        }
    }
    
    // figure actual time
    switch (how) {
        case '24hr:
            return .ctime(time).subrange(12, 5);
        case '24hr_sec:
            return .ctime(time).subrange(12, 8);
        case '12hr:
            time = .hr_min_sec(time);
            hrs = toint(time[1]);
            ampm = hrs < 12 ? ampm[1] | ampm[2];
            hrs = hrs % 12;
            hrs = hrs == 0 ? 12 | hrs;
            return tostr(abs(hrs)) + sep2 + time[2] + ampm;
        case '12hr_sec:
            time = .hr_min_sec(time);
            hrs = toint(time[1]);
            ampm = hrs < 12 ? ampm[1] | ampm[2];
            hrs = hrs % 12;
            hrs = hrs == 0 ? 12 | hrs;
            return tostr(hrs) + sep2 + time[2] + sep2 + time[3] + ampm;
    }
.

public day_str
    arg [args];
    var day, days, d, time, option;
    
    if (args && type(args[1]) != 'integer) {
        time = time();
        option = [@args, 'long][1];
    } else {
        time = [@args, time()][1];
        option = [@args, 'long, 'long][2];
    }
    if (type(time) == 'integer)
        day = (ctime(time).explode())[1];
    else
        day = (time.explode())[1];
    days = .days();
    switch (option) {
        case 'num:
            return .month_day_str();
        case 'short:
            return day;
        default:
            for d in (days) {
                if ($string.match_begin(d, day))
                    return d;
            }
    }
.

public month_day_str
    arg [time];
    
    time = .ctime([@time, .time()][1]);
    return time.subrange(9, 2).replace(" ", "0");
.

public hr_min_sec
    arg [time];
    var ctime;
    
    ctime = .ctime(@time);
    return [ctime.subrange(12, 2), ctime.subrange(15, 2), ctime.subrange(18, 2)];
.

public time_stamp
    return .ldate('date) + "/" + $time.ltime('24hr_sec);
.

public to_english
    arg time, [reftime];
    var times, words, x, seconds, ctime, months, month, year, days, out;
    
    // most of this was stolen from MOO (und ve are evil)
    if (time < 1)
        return "0 seconds";
    reftime = reftime || time();
    ctime = type(reftime) == 'integer ? ctime(reftime) | reftime;
    words = ["year", "month", "day", "hour", "minute", "second"];
    times = [];
    seconds = [60, 60, 24];
    for x in (seconds) {
        times = [time % x, @times];
        time = time / x;
    }
    months = 0;
    month = (.months())[ctime.subrange(5, 3)][2];
    year = toint(.year_str(reftime));
    days = (.months())[ctime.subrange(5, 3)][3];
    while (time >= days + (month == 2 && year % 4 == 0 && !(year % 400 in [100, 200, 300]))) {
        time = time - days;
        months = months + 1;
        month = month + 1;
        if (month > 12) {
            year = year + 1;
            month = 1;
        }
    }
    times = [months / 12, months % 12, time, @times];
    out = [];
    for x in [1 .. words.length()] {
        if (times[x] > 0)
            out = [@out, tostr(times[x]) + " " + words[x] + (times[x] == 1 ? "" | "s")];
    }
    return $list.to_english(out);
.

public date
    arg [args];
    var time, opt;
    
    time = [@args, time()][1];
    opt = [@args, 'long, 'long][2];
    if (type(time) != 'integer)
        throw(~type, "Time must be submitted as an integer.");
    switch (opt) {
        case 'short:
            return ctime(time);
        default:
            return .ltime(time) + ", " + .ldate(time);
    }
.



parent $time_root
object $dark_time

var $root inited 1
var $root objname 'dark_time
var $root created_on 796268969
var $root flags ['parameters, 'methods, 'code, 'core]
var $time_root created_on 695533749
var $time_root hours_per_day 21
var $time_root days_per_year 345
var $time_root year_begin 425
var $time_root secs_per_min 60
var $time_root secs_per_hour 4320
var $time_root secs_per_day 90720
var $time_root secs_per_week 453600
var $time_root secs_per_year 41731200
var $time_root mins_per_hour 72
var $time_root standard 0

public paradise_time
    arg [args];
    var time, hour, mde;
    
    // args: terran time() == output of server builtin.
    //       'seconds      == with seconds
    //       'no_mde       == with morning/day/evening hour pre-pension
    if (args && type(args[1]) == 'integer) {
        time = .convert(args[1]);
        args = args.delete(1);
    } else {
        time = .time();
    }
    hour = .hour(time);
    mde = hour > 7 ? hour > 14 ? "eh " | "dh " | "mh ";
    if (args && 'no_mde in args)
        mde = "";
    hour = hour % 7;
    hour = hour == 0 ? 7 | hour;
    if (args && 'seconds in args)
        return mde + tostr(hour) + ":" + tostr(.minute(time)).pad(2, "0") + ":" + tostr(.second(time)).pad(2, "0");
    else
        return mde + tostr(hour) + ":" + tostr(.minute(time)).pad(2, "0");
.

public ilraitheen_time
    arg [args];
    var time;
    
    // args: terran time() == output of server builtin.
    //       'seconds      == with seconds
    if (args && type(args[1]) == 'integer) {
        time = .convert(args[1]);
        args = args.delete(1);
    } else {
        time = .time();
    }
    if (args && type(args[1]) == 'symbol)
        return tostr(.hour(time)) + ":" + tostr(.minute(time)).pad(2, "0") + ":" + tostr(.second(time)).pad(2, "0");
    else
        return tostr(.hour(time)) + ":" + tostr(.minute(time)).pad(2, "0");
.



parent $mail_root
object $mail_envelope

var $root objname 'mail_envelope
var $root created_on 800473788
var $root inited 1
var $root flags ['parameters, 'methods, 'code, 'core]



parent $mail_root
object $mail_address

var $root objname 'mail_address
var $root created_on 800473783
var $root inited 1
var $root flags ['parameters, 'methods, 'code, 'core]



parent $db
object $registry

var $root fertile 1
var $root inited 1
var $root objname 'registry
var $root created_on 796268969
var $root flags ['parameters, 'methods, 'code, 'fertile, 'core]
var $db database #[]
var $registry stripped_characters ""
var $registry min_char_len 0
var $registry max_char_len 0
var $registry max_word_len 0
var $registry reserved_names []
var $registry invalid_names []

root uninit_registry
    reserved_names = 0;
    invalid_names = 0;
    trusted = 0;
    stripped_characters = 0;
.

public set_max_word_len
    arg value;
    
    .perms(sender(), 'manager);
    if (type(value) != 'integer)
        throw(~type, "Value is not an integer");
    max_word_len = value;
.

public set_max_char_len
    arg value;
    
    .perms(sender(), 'manager);
    if (type(value) != 'integer)
        throw(~type, "Value is not an integer");
    max_char_len = value;
.

public set_min_char_len
    arg value;
    
    .perms(sender(), 'manager);
    if (type(value) != 'integer)
        throw(~type, "Value is not an integer");
    min_char_len = value;
.

public set_stripped_characters
    arg string;
    
    .perms(sender(), 'manager);
    if (type(string) != 'string)
        throw(~type, "Value is not a string.");
    stripped_characters = string;
.

public insert
    arg name, obj;
    
    // registers obj with obj.name
    //  .perms(caller(), 'trusts);
    if (stripped_characters)
        name = $string.strip(name, stripped_characters);
    (> pass(name, obj) <);
.

public remove
    arg name;
    
    // removes the object from the database. 
    // THIS: is what is broken with guests, should fix it.
    // .perms(caller(), 'trusts);
    if (stripped_characters)
        name = $string.strip(name, stripped_characters);
    (> pass(name) <);
.

public database
    if (!.has_flag('parameters, sender()))
        throw(~perm, "Database is not readable by sender.");
    return (> pass() <);
.

public exact_match
    arg name;
    
    // returns a direct match of the name (if there is one)
    if (!.has_flag('parameters, sender()))
        throw(~perm, "Database is not readable by sender.");
    if (stripped_characters)
        name = $string.strip(name, stripped_characters);
    return (> pass(name) <);
.

public valid_name
    arg name;
    var word, sname;
    
    // returns 1 if the name is valid
    /// if (!.has_flag('parameters,sender()))
    //    throw(~perm, "Database is not readable by sender.");
    (> .perms(caller(), 'trusts) <);
    
    // check name itself first
    sname = name;
    if (stripped_characters)
        sname = $string.strip(name, stripped_characters);
    if (max_word_len && name.explode().length() > max_word_len)
        throw(~invname, "Names can only be " + tostr(max_word_len) + " words long.");
    if (min_char_len && sname.length() < min_char_len)
        throw(~invname, "Names must have at least " + tostr(min_char_len) + " alpha-numeric characters in them");
    if (max_char_len && name.length() > max_char_len)
        throw(~invname, "Names can only be " + tostr(max_char_len) + " characters long.");
    
    // see if it already exists
    if ((| .exact_match(name) |)) {
        if (!((.database())[sname] == sender()))
            throw(~invname, "Name already exists.");
    }
    
    // check reserved and invalid names
    for word in (name.explode()) {
        if (reserved_names && word in reserved_names)
            throw(~invname, "`" + word + "' is a reserved name.");
        if (invalid_names) {
            for word in (invalid_names) {
                if ($string.match_pattern("*" + word + "*", name))
                    throw(~invname, "`" + word + "' is not allowed as part of a name.");
            }
        }
    }
.

public match_begin
    arg name;
    var matches, obj;
    
    // returns a direct match, or partial matches
    if (stripped_characters)
        name = $string.strip(name, stripped_characters);
    return (> pass(name) <);
.

public stripped_characters
    return stripped_characters;
.

public key_changed
    arg old_name, new_name;
    
    // adjusts the database for the new name
    .perms(caller(), 'trusts);
    if (stripped_characters) {
        old_name = $string.strip(old_name, stripped_characters);
        new_name = $string.strip(new_name, stripped_characters);
    }
    (> pass(old_name, new_name) <);
.

public find
    arg name;
    var tmp;
    
    if (.stripped_characters())
        name = $string.strip(name, .stripped_characters());
    name || throw(~namenf, "No matches found.");
    tmp = (| .exact_match(name) |);
    if (tmp)
        return tmp;
    catch any {
        tmp = (> .match_begin(name) <);
    } with handler {
        switch (error()) {
            case ~ambig:
                rethrow(error());
            default:
                throw(~namenf, "No matches found.");
        }
    }
    return tmp;
.



parent $registry
parent $mail_root
object $mail_db

var $root trusted []
var $root inited 1
var $root objname 'mail_db
var $root created_on 796268969
var $root indestructable 1
var $root flags ['parameters, 'methods, 'code, 'core]
var $db database #[["news", $mail_list_news], ["bugs", $mail_list_bugs]]

public valid_recipient
    arg recip;
    
    if (recip.has_ancestor($mail_list))
        return 1;
    return 0;
.

public mail_name
    arg obj;
    
    return "*" + obj.namef();
.



parent $user_parsers
object $null_parser

var $root inited 1
var $root objname 'null_parser
var $root created_on 796268969
var $root flags ['parameters, 'methods, 'code, 'core]

public parse
    arg user, str, [anything_else];
    var i;
    
    for i in [1 .. str.length()] {
        if (str[i] != " ")
            return 'failed;
    }
    return 'ok;
.



parent $user_parsers
object $command_aliases_parser

var $root trusted []
var $root inited 1
var $root objname 'command_aliases_parser
var $root created_on 796268969
var $root flags ['parameters, 'methods, 'code, 'core]

public parse
    arg user, str, next_parser, [other_parsers];
    var alias, match, i, result;
    
    for alias in (user.command_aliases()) {
        match = $string.match_pattern(alias[1], str);
        if (match != 0) {
            str = alias[2];
            for i in [1 .. match.length()]
                str = str.replace("%" + tostr(i), match[i]);
            break;
        }
    }
    result = next_parser.parse(user, str, @other_parsers);
    if (match != 0 && result == 'failed)
        return "Command converted to \"" + str + "\" but not understood.";
    return result;
.



parent $user_parsers
object $command_parser

var $root objname 'command_parser
var $root created_on 796680318
var $root inited 1
var $root indestructable 1
var $root flags ['parameters, 'methods, 'code, 'core]

public parse
    arg u, str, next_parser, [other_parsers];
    var l, cmd, c, p, obj;
    
    while (str && str[1] == " ")
        str = str.subrange(2);
    if (str) {
        cmd = str.explode();
        cmd = [str, cmd[1], cmd.subrange(2).to_string() || ""];
        p = [];
        if (c = (| u.match_in_shortcut_cache(@cmd) |)) {
            if (c[1] == 'shortcut)
                return .shortcut(u, @c[2]);
            p = c[2];
        }
        if (c = (| u.match_in_local_cache(@cmd) |)) {
            if (c[1] == 'local)
                return .local(u, @c[2]);
            p = p + c[2];
        }
        if (c = (| u.match_in_remote_cache(@cmd) |)) {
            if (c[1] == 'remote)
                return .remote(u, @c[2]);
            p = p + c[2];
        }
        l = u.location();
        if (c = (| l.match_in_locl_cache(@cmd) |)) {
            if (c[1] == 'remote)
                return .remote(u, @c[2]);
            p = p + c[2];
        }
        if (c = (| l.match_in_remote_cache(@cmd) |)) {
            if (c[1] == 'remote)
                return .remote(u, @c[2]);
            p = p + c[2];
        }
        if (c = (| .grasp_for_remote_command(@cmd) |))
            return .remote(u, @c[2]);
        for obj in ((| u.location().exits() |) || []) {
            if (obj.match_name(str))
                return [obj, 'invoke];
        }
        if (p)
            return .partial(u, cmd, p);
    }
    return next_parser.parse(u, str, @other_parsers);
.

public partial
    arg user, args, templates;
    var part, line;
    
    for part in (templates)
        templates = templates.replace(part in templates, toliteral(part));
    if (templates.length() == 1)
        line = toliteral(args[2]) + " could match ";
    else if (templates.length() == 2)
        line = toliteral(args[2]) + " could match either ";
    else
        line = toliteral(args[2]) + " could match any of ";
    return line + templates.to_english("", " or ") + ".";
.

public shortcut
    arg user, method, parsed;
    
    return [user, method, @parsed];
.

public complete
    arg cmd, user, obj, match, info;
    var x, method, parsed;
    
    method = info[2];
    info = info[4];
    parsed = info.keys();
    for x in [1 .. match.length()] {
        if (x in parsed)
            match = match.replace(x, (> $command_lib.convert_arg(info[x][1], match[x], user, info[x][2] ? info[x][2] | user, user) <));
    }
    return [user, method, (cmd.explode())[1], @match];
.

public grasp_for_remote_command
    arg str, cmd, args;
    var reg, obj, cdef, match, matched, pick, info;
    
    reg = $string.match_regexp("[$#][a-z_0-9][a-z_0-9]*", args);
    if (!reg)
        return 0;
    obj = (| $object_lib.to_dbref(args.subrange(@reg[1])) |);
    if (!obj)
        return 0;
    info = (| obj.get_command_info('remote, cmd) |);
    if (!info)
        return 0;
    matched = [];
    for cdef in (info) {
        match = $string.match_template(cdef[2], args);
        if (match != 0)
            matched = matched + [[match.length(), obj, [str, cmd, @match], @cdef.subrange(3)]];
    }
    if (matched) {
        pick = matched[1];
        matched = matched.delete(1);
        for match in (matched) {
            if (match[1] > pick[1])
                pick = match;
        }
        return ['remote, pick.delete(1)];
    }
    return ['partial, [[str, cmd], info.slice(3)]];
.

public local
    arg user, [matches];
    var parsed, match;
    
    parsed = [];
    for match in (matches) {
        match = ._local(user, @match);
        if (match[1] == 'match)
            return match.delete(1);
        parsed = [match[2]] + parsed;
    }
    return 0;
.

public remote
    arg user, [matches];
    var parsed, match;
    
    parsed = [];
    for match in (matches) {
        match = ._remote(user, @match);
        if (match[1] == 'match)
            return match.delete(1);
        parsed = [match[2]] + parsed;
    }
    return parsed[1];
.

public handle_error
    arg traceback;
    
    return traceback[1][2];
.

public _remote
    arg user, nmatch, definer, match, template, method, info;
    var x, parsed, str, value, that, tinfo, new, c;
    
    parsed = info.keys();
    catch any {
        for x in [1 .. nmatch] {
            if (x in parsed) {
                if (info[x][1] == 'this) {
                    value = (> user.match_environment(match[x + 2]) <);
    
                    // talk about a hack around, hope its not a prob
                    if (!value.has_ancestor(definer)) {
                        tinfo = (| value.get_command_info('remote, match[2]) |);
                        if (!tinfo || tinfo[1] != 'remote)
                            return ['error, value.name('def).capitalize() + " does not define remote command \"" + template + "\""];
    
                        // mebbe we'll get lucky...
                        tinfo = tinfo[2];
                        for c in (tinfo) {
                            if (c[3] == template) {
                                new = 1;
                                definer = value;
                                method = c[4];
                            }
                        }
    
                        // we should only do this once
                        if (!new)
                            return ['error, value.name('def).capitalize() + " does not define remote command \"" + template + "\""];
                    }
                    that = value;
                } else {
                    value = (> .convert_arg(info[x][1], match[x + 2], user, info[x][2] ? info[x][2][1] | definer, user) <);
                }
                match = match.replace(x + 2, value);
            }
        }
    } with handler {
        return ['error, .handle_error(traceback())];
    }
    if (!that)
        return ['error, "An error was encountered: no target object found."];
    return ['match, that, method, @match];
.

public _local
    arg user, nmatch, match, template, method, info;
    var x, parsed, str;
    
    parsed = info.keys();
    catch any {
        for x in [1 .. match.length()] {
            if (x in parsed)
                match = match.replace(x + 2, (> .convert_arg(info[x][1], match[x + 2], user, info[x][2] ? info[x][2][1] | user, user) <));
        }
    } with handler {
        ['error, .handle_error(traceback())];
    }
    return ['match, user, method, @match];
.

public convert_arg
    arg type, str, me, ancestor, target;
    var obj;
    
    switch (type) {
        case 'any:
            return str;
        case 'object:
            return (> me.match_environment(str) <);
        case 'objref:
            return (> $parse_lib.xreference(str, me, [me, 'match_environment, []]) <);
        case 'user:
            if (str == "me")
                return me;
            return (> $user_db.find(str) <);
        case 'number:
            return (> str.to_number() <);
        case 'descendant:
            obj = (> me.match_environment(str) <);
            if (!obj.has_ancestor(ancestor))
                throw(~parse, obj.name() + " is not descended from " + ancestor.name() + "!");
            return obj;
    }
.



parent $user_parsers
object $conference_parser

var $root trusted []
var $root inited 1
var $root objname 'conference_parser
var $root created_on 796268969
var $root flags ['parameters, 'methods, 'code, 'core]

public parse
    arg user, str, next_parser, [other_parsers];
    
    if (str && str[1] == ">")
        str = str.subrange(2);
    else
        str = "say " + str;
    return next_parser.parse(user, str, @other_parsers);
.



parent $filters
object $epic_filter

var $root inited 1
var $root objname 'epic_filter
var $root created_on 796268969
var $root flags ['parameters, 'methods, 'code, 'core]

public old_filter
    arg input;
    var output, x, z, line;
    
    // this filter MUST be first in the filters list..
    output = [];
    input = .compress(input);
    for z in (input) {
        line = [];
        z = ($string.explode_delimited(z, "<", ">"))[1];
        for x in (z) {
            if (type(x) == 'string)
                line = [@line, x];
        }
        output = [@output, $list.to_string(line, "")];
    }
    return output;
.

public filter
    arg what;
    var tags;
    
    $jeff.debug('filter, what);
    if (type(what) != 'frob)
        return what;
    if (!(class(what) in [$ctext]))
        return what;
    $jeff.debug('frob);
    what = what.evaluate(sender());
    $jeff.debug(what);
    return what;
.



parent $filters
object $wrap_filter

var $root inited 1
var $root objname 'wrap_filter
var $root created_on 796268969
var $root flags ['parameters, 'methods, 'code, 'core]

public filter
    arg input;
    var len, output, e, line;
    
    len = sender().linelen();
    output = [];
    input = .compress(input);
    for e in (input) {
        e = $string.wrap_line(e, len);
        output = [@output, e[1]];
        for line in (e.subrange(2))
            output = [@output, " " + line];
    }
    return output;
.



parent $filters
object $ctext_filter

var $root inited 1
var $root objname 'ctext_filter
var $root created_on 796268969
var $root flags ['parameters, 'methods, 'code, 'core]

public filter
    arg what, [defaults];
    var dic, output;
    
    dic = [@defaults, #[]][1].union(#[['receiver, sender()], ['time, 'post], ['formatter, $cml2_telnet_format]]);
    switch (class(what)) {
        case $message_frob:
            output = what.format(dic);
        case $ctext2_frob:
            what = what.set_vars(dic);
            output = what.format();
        case $ctext_frob:
            output = what.eval_ctext(dic);
        case $ctext_format:
            output = dic['formatter].eval_ctext(what, dic);
        default:
            output = what;
    }
    return output;
.



parent $cml2
object $cml2_evaluator

var $root objname 'cml2_evaluator
var $root child_index 3
var $root created_on 806300471
var $root inited 1
var $root flags ['parameters, 'methods, 'code, 'core]

public eval_ctext
    arg data, [vars];
    var ret;
    
    vars = [@vars, #[]][1];
    vars = vars.add('line, 1);
    ret = ._eval_ctext(vars, data);
    return $ctext2_frob.new(ret[1], ret[2].add('evaluator, this()));
.

public eval_generator
    arg vars, gen;
    var flags, key, value, ret, name;
    
    flags = gen.ctext_uflags();
    for key in (flags.keys()) {
        ret = ._eval_ctext(vars, flags[key]);
        gen = gen.add_ctext_flag(key, ret[1]);
        vars = ret[2];
    }
    catch ~methodnf {
        ret = .(gen.method())(vars, gen.ctext_flags(), gen.args());
        return [ret[1], ret[2]];
    } with handler {
        catch ~keynf {
            return [[vars[gen.name()]], vars];
        } with handler {
            return [[">>ERROR: Unknown generator [" + gen.name() + "] on line " + tostr(vars['line]) + ".<<"], vars];
        }
    }
.

public eval_formator
    arg vars, form;
    var flags, ret, key;
    
    if (vars['time] == 'format)
        return vars['formatter]._eval_ctext(vars, form);
    flags = form.ctext_uflags();
    for key in (flags.keys()) {
        ret = ._eval_ctext(flags[key], vars);
        form = form.add_ctext_flag(key, ret[1]);
        vars = ret[2];
    }
    catch ~methodnf {
        return .(tosym("do_" + form.name()))(vars, form.ctext_flags(), form.args());
    } with handler {
        ret = ._eval_ctext(vars, form.args());
        form = form.set_args(ret[1]);
        vars = ret[2];
        return [[form], vars];
    }
.

public _eval_ctext
    arg vars, data;
    var out, uflags, ret, token;
    
    out = [];
    if (type(data) != 'list)
        data = [data];
    for token in (data) {
        if (type(token) == 'frob) {
            switch (class(token)) {
                case $ctext_generator:
                    ret = .eval_generator(vars, token);
                    out = [@out, @ret[1]];
                    vars = ret[2];
                case $ctext_format:
                    ret = .eval_formatter(vars, token);
                    if (vars['time] == 'format)
                        out = out.join(ret[1]);
                    else
                        out = [@out, @ret[1]];
                    vars = ret[2];
                default:
                    out = [@out, token];
            }
        } else {
            out = [@out, token];
        }
    }
    return [out, vars];
.

public _eval_ctext_list
    arg vars, data;
    var out, uflags, ret, token;
    
    out = [];
    while (data) {
        token = data[1];
        data = data.subrange(2);
        if (type(token) == 'frob) {
            if (class(token) == $ctext_generator) {
                ret = .eval_generator(vars, token);
                out = [@out, ret[1]];
                vars = ret[2];
            } else if (class(token) == $ctext_format) {
                ret = .eval_formatter(vars, token);
                out = [@out, ret[1]];
                vars = ret[2];
            } else {
                out = [@out, token];
            }
        } else {
            out = [@out, token];
        }
    }
    return [out, data, vars];
.

public init
    return #[['line, 1]];
.

public do_group
    arg vars, flags, args;
    var out, token, ret;
    
    out = [];
    for token in (args) {
        ret = ._eval_ctext(vars, token);
        if (out.length() && type(out.last()) == 'string && type((| ret[1][1] |) || ret[1]) == 'string)
            out = out.join(ret[1]);
        else
            out = [@out, @ret[1]];
        vars = ret[2];
    }
    return [out, vars];
.



parent $cml2
object $cml2_form

var $root objname 'cml2_form
var $root created_on 806551381
var $root inited 1
var $root flags ['parameters, 'methods, 'code, 'core]
var $cml2_form options #[["ul", []], ["strong", []], ["pre", []], ["dl", []], ["bold", []], ["anchor", []], ["em", []], ["link", []], ["web", []], ["hr", []], ["tt", []]]
var $cml2_form fields #[["ul", ["lh", "li"]], ["strong", []], ["pre", []], ["dl", ["lh", "dt", "dd"]], ["bold", []], ["anchor", []], ["em", []], ["header", []], ["link", []], ["web", []], ["name", []], ["tt", []], ["action", []], ["subj", []], ["li", []], ["dt", []], ["dd", []]]

public blocks
    return fields.keys();
.

public is_block
    arg tag;
    
    return tag in fields.keys();
.

public fields
    arg tag;
    
    return fields[tag];
.

public has_field
    arg tag, field;
    
    return field in fields[tag];
.

public options
    arg tag;
    
    return options.keys();
.

public has_option
    arg tag, opt;
    
    return opt in options.keys();
.

public add_block
    arg b;
    
    fields = fields.add(b, []);
.

public del_block
    arg b;
    
    fields = fields.del(b);
.

public add_field
    arg b, f;
    
    fields = fields.add_elem(b, f);
.

public del_field
    arg b, f;
    
    fields = fields.del_elem(b, f);
.

public add_tag
    arg t;
    
    options = options.add(t, []);
.

public del_tag
    arg t;
    
    (| fields = fields.del(t) |);
    options = options.del(t);
.

public add_option
    arg t, o;
    
    options = options.add_elem(t, o);
.

public del_option
    arg t, o;
    
    options = options.del_elem(t, o);
.

public tags
    return options.keys();
.

public is_field
    arg field;
    var f, b;
    
    f = [];
    for b in (fields.values())
        f = f.union(b);
    return field in f;
.



parent $cml2
object $cml2_compiler

var $root objname 'cml2_compiler
var $root created_on 806018078
var $root inited 1
var $root flags ['parameters, 'methods, 'code, 'core]

public compile_cml
    arg text;
    var vars, ret;
    
    vars = #[['line, 1], ['this, sender()]];
    text = .tokenize(text);
    ret = (> .parse_string(vars, text) <);
    return $ctext2_frob.new(ret[1], ret[3]);
.

public parse_string
    arg vars, tokens;
    var out, ret, word, token;
    
    word = "";
    out = [];
    while (tokens) {
        token = tokens[1];
        tokens = tokens.subrange(2);
        if (token in ["{", "["]) {
            if (word.length())
                out = [@out, word];
            word = "";
            ret = .parse_command(vars, tokens, token);
            out = [@out, @ret[1]];
            tokens = ret[2];
            vars = ret[3];
        } else if (token == "" || token[1] == " ") {
            if (word.length() || out.length())
                word = word + " ";
        } else if (token in ["]", "}"]) {
            if (word.length())
                out = [@out, word];
            return [out, tokens, vars];
        } else {
            word = word + token;
        }
        pause();
    }
    if (word.length())
        out = [@out, word];
    return [out, tokens, vars];
.

public parse_top
    arg vars, tokens;
    var out, ret, word, token;
    
    tokens = ._eat_space(tokens);
    out = [];
    while (tokens) {
        token = tokens[1];
        tokens = tokens.subrange(2);
        switch (token) {
            case "{", "[":
                ret = (> .parse_command(vars, tokens, token) <);
                out = [@out, @ret[1]];
                tokens = ret[2];
                vars = ret[3];
            case "]", "}":
                return [out, tokens, vars];
            case "\"", "\\:":
                out = out.join(token);
            default:
                out = [@out, token];
        }
        if (text.length() && tokens[1][1] == " ") {
            out = out.join(" ");
            token = token.subrange(2);
        }
    }
    return [out, tokens, vars];
.

public parse_command
    arg vars, text, token;
    var ret, name, flags, args, end;
    
    name = "";
    flags = [];
    args = [];
    text = ._eat_space(text);
    name = text[1];
    if (name == ":")
        name = "group";
    else
        text = text.subrange(2);
    text = ._eat_space(text);
    if (!(text[1] in ["]", "}", ":"])) {
        ret = .parse_flags(vars, text);
        flags = ret[1];
        text = ret[2];
        text = ._eat_space(text);
        vars = ret[3];
    }
    if (text[1] == ":") {
        if (token == "[")
            ret = .parse_top(vars, text.subrange(2));
        else
            ret = .parse_string(vars, text.subrange(2));
        args = ret[1];
        text = ret[2];
        vars = ret[3];
    } else if (token == "{" && $cml2_form.is_block(name)) {
        ret = .parse_nested(vars, text.subrange(2), name);
        args = ret[1];
        text = ret[2];
        vars = ret[3];
    } else {
        text = text.subrange(2);
    }
    if (token == "{")
        ret = ._make_form(vars, name, flags, args);
    else
        ret = ._make_gen(vars, name, flags, args);
    return [ret[1], text, ret[2]];
.

public parse_flags
    arg vars, tokens;
    var flags, flag, token, set, ret;
    
    set = 0;
    flag = "";
    flags = [];
    while (tokens) {
        token = tokens[1];
        tokens = tokens.subrange(2);
        switch (token) {
            case "}", "]", ":":
                return [flags, [token, @tokens], vars];
            case "=":
                catch ~range {
                    flag = flags.last();
                } with handler {
                    throw(~parse, "key=value flag with no key.");
                }
                flags = flags.chop();
                if (type(flag) == 'list)
                    throw(~parse, "key=value flag with no key.");
                set = 1;
            default:
                if (set) {
                    switch (token) {
                        case "{", "[":
                            ret = .parse_command(vars, tokens, token);
                            token = ret[1];
                            tokens = ret[2];
                        case "\"":
                            ret = .parse_string(vars, tokens);
                            token = ret[1];
                            tokens = ret[2];
                    }
                    flags = [@flags, [flag, token]];
                    flag = "";
                    set = 0;
                } else {
                    flags = [@flags, token];
                }
        }
    }
.

public tokenize
    arg text;
    var spaces, word, out, escaped, char, i;
    
    //break text into a list of tokens.
    if (type(text) == 'list)
        text = text.to_string("{_ln}");
    out = [];
    word = "";
    spaces = "";
    escaped = 0;
    for i in [1 .. text.length()] {
        char = text[i];
        if (escaped) {
            escaped = 0;
            word = word + char;
        } else {
            switch (char) {
                case "\\":
                    escaped = 1;
                    if (spaces.length()) {
                        out = [@out, spaces];
                        spaces = " ";
                    }
                case " ":
                    if (!spaces.length()) {
                        if (word.length())
                            out = [@out, word];
                        word = "";
                    }
                    spaces = spaces + " ";
                case "{", "}", "[", "]", "=", "\"", ":":
                    if (spaces.length()) {
                        out = [@out, spaces];
                        spaces = "";
                    }
                    if (word.length())
                        out = [@out, word, char];
                    else
                        out = [@out, char];
                    word = "";
                default:
                    if (spaces.length()) {
                        out = [@out, spaces];
                        spaces = "";
                    }
                    word = word + char;
            }
        }
        pause();
    }
    if (spaces.length()) {
        out = [@out, spaces];
        spaces = "";
    } else if (word.length()) {
        out = [@out, word];
        word = "";
    }
    if (word.length())
        return [@out, word];
    return out;
.

public parse_list
    arg tokens;
    var out, ret, token;
    
    out = [];
    while (tokens) {
        token = tokens[1];
        tokens = tokens.subrange(2);
        if (token in ["{", "["]) {
            ret = .parse_command(tokens);
            out = [@out, @ret[1]];
            tokens = ret[2];
        } else if (token in ["]", "}"]) {
            return [out, [token, @tokens]];
        } else {
            out = [@out, token];
        }
    }
    if (word.length())
        out = [@out, word];
    return [out, tokens];
.

public parse_nested
    arg vars, tokens, name;
    var out, ret, token, word, start_line;
    
    if (name == "pre")
        return ._parse_pre(vars, tokens);
    start_line = vars['line];
    word = "";
    out = [];
    tokens = ._eat_space(tokens);
    while (tokens) {
        token = tokens[1];
        tokens = tokens.subrange(2);
        if (token in ["{", "["]) {
            if (word.length()) {
                out = [@out, word];
                word = "";
            }
            ret = .parse_command(vars, tokens, token);
            tokens = ret[2];
            out = [@out, @ret[1]];
            vars = ret[3];
            if (type(out.last()) == 'frob && class(out.last()) == $ctext_format && out.last().end()) {
                if (out.last().name().subrange(2) == name)
                    return [out.chop(), tokens, vars];
                else
                    throw(~parse, "{" + name + "} at line " + tostr(start_line) + " ended by {" + out.last().name() + "} at line " + tostr(vars['line]) + ".");
            }
        } else {
            if (word.length())
                word = word + " ";
            if (token[1] != " ")
                word = word + token;
        }
        pause();
    }
    return [out, tokens, vars];
.

public _make_gen
    arg vars, name, flags, args;
    var ret;
    
    catch ~methodnf {
        ret = .(tosym("gen_" + name))(vars, flags, args);
        return [ret[1], ret[2]];
    } with handler {
        return [[$ctext_generator.new(name, flags, args)], vars];
    }
.

public _make_form
    arg vars, name, flags, args;
    var ret;
    
    catch ~methodnf {
        ret = .(tosym("do_" + name))(vars, flags, args);
        return [ret[1], ret[2]];
    } with handler {
        return [[$ctext_format.new(name, flags, args)], vars];
    }
.

public _eat_space
    arg text;
    
    if (!text.length())
        return text;
    if (text[1][1] == " ")
        return text.subrange(2);
    return text;
.

public _parse_pre
    arg vars, tokens;
    var out, ln, pos, line, token;
    
    out = [];
    line = "";
    ln = $ctext_format.new("_ln", [], []);
    while (tokens) {
        token = tokens[1];
        tokens = tokens.subrange(2);
        if (tokens.length() >= 2 && token == "{" && tokens[2] == "}") {
            if (tokens[1] == "-pre") {
                if (line)
                    out = [@out, line];
                return [out, tokens.subrange(3), vars];
            } else if (tokens[1] == "_ln") {
                vars = vars.add('line, vars['line] + 1);
            } else {
                line = line + token;
            }
        } else {
            line = line + token;
        }
    }
    throw(~parse, "{pre} unended.");
.

public do__ln
    arg vars, flags, args;
    
    return [[" "], vars.add('line, vars['line] + 1)];
.

public _make_form2
    arg vars, name, flags, args;
    var ret;
    
    catch ~methodnf {
        ret = .(tosym("do_" + name))(vars, flags, args);
        return [ret[1], ret[2]];
    } with handler {
        return [[$ctext_format.new(name, flags, args)], vars];
    }
.

public do_link
    arg vars, flags, args;
    var links, item, node;
    
    links = (| vars['links] |) || #[];
    for item in (flags) {
        if (type(item) == 'list && item[1] == "node") {
            node = item[2];
            break;
        }
    }
    if (!node)
        throw(~parse, "No node for {link} on line " + tostr(vars['line]) + ".");
    links = links.add(args[1], node);
    return [[$ctext_format.new("link", flags, args)], vars.add('links, links)];
.

public gen_group
    arg vars, flags, args;
    
    return [args, vars];
.



parent $logic_frob
object $xor

var $root trusted []
var $root inited 1
var $root objname 'xor
var $root created_on 796268969
var $root flags ['parameters, 'methods, 'code, 'core]

public unparse
    arg xorlist;
    var str, x;
    
    str = "";
    for x in (xorlist) {
        catch any {
            str = str + x.unparse() + " ^^ ";
        } with handler {
            str = str + tostr(x) + " ^^ ";
        }
    }
    return "(" + (str && str.subrange(1, str.length() - 4)) + ")";
.

public test
    arg xorlist, [args];
    var val, x;
    
    val = 0;
    for x in (xorlist) {
        catch ~type, ~methodnf {
            val = val ? !x.test(@args) | x.test(@args);
        } with handler {
            val = val ? !x | x;
        }
    }
    return val;
.



parent $logic_frob
object $and

var $root trusted []
var $root inited 1
var $root objname 'and
var $root created_on 796268969
var $root flags ['parameters, 'methods, 'code, 'core]

public unparse
    arg andlist;
    var str, x;
    
    str = "";
    for x in (andlist) {
        catch any {
            str = str + x.unparse() + " && ";
        } with handler {
            str = str + tostr(x) + " && ";
        }
    }
    return "(" + (str && str.subrange(1, str.length() - 4)) + ")";
.

public test
    arg andlist, [args];
    var val, x;
    
    val = 0;
    for x in (andlist) {
        catch ~type, ~methodnf {
            val = x.test(@args);
        } with handler {
            val = x;
        }
        if (!val)
            break;
    }
    return val;
.



parent $logic_frob
object $lock_frob

var $root child_index 2
var $root objname 'lock_frob
var $root created_on 796268969
var $root flags ['parameters, 'methods, 'code, 'core]
var $named name ['uniq, "lock_frob"]
var $named name_aliases []
var $lock_frob names 0

public lock_name
    arg value, [type];
    
    type = [@type, 'default][1];
    if (type == 'literal)
        type = 'default;
    return (| names[type] |) || names['default];
.

public set_lock_name
    arg name;
    
    (> .perms(sender(), 'manager) <);
    lock_name = name;
.

public add_name
    arg type, name;
    
    (> .perms(sender(), 'manager) <);
    if (!names)
        names = #[];
    names = names.add(type, name);
.



parent $logic_frob
object $not

var $root trusted []
var $root inited 1
var $root objname 'not
var $root created_on 796268969
var $root flags ['parameters, 'methods, 'code, 'core]

public unparse
    arg notlist;
    
    catch any {
        catch ~type, ~methodnf {
            return "!" + notlist[1].unparse();
        } with handler {
            return "!" + tostr(notlist[1]);
        }
    } with handler {
        return "!()";
    }
.

public test
    arg notlist, [args];
    var val;
    
    catch ~range {
        catch ~type, ~methodnf {
            return !notlist[1].test(@args);
        } with handler {
            return !notlist[1];
        }
    } with handler {
        return 1;
    }
.



parent $logic_frob
object $or

var $root trusted []
var $root inited 1
var $root objname 'or
var $root created_on 796268969
var $root flags ['parameters, 'methods, 'code, 'core]

public unparse
    arg orlist;
    var str, x;
    
    str = "";
    for x in (orlist) {
        catch any {
            str = str + x.unparse() + " || ";
        } with handler {
            str = str + tostr(x) + " || ";
        }
    }
    return "(" + (str && str.subrange(1, str.length() - 4)) + ")";
.

public test
    arg orlist, [args];
    var val, x;
    
    val = 0;
    for x in (orlist) {
        catch ~type, ~methodnf {
            val = x.test(@args);
        } with handler {
            val = x;
        }
        if (val)
            break;
    }
    return val;
.



parent $logic_frob
object $true

var $root trusted []
var $root inited 1
var $root objname 'true
var $root created_on 796268969
var $root flags ['parameters, 'methods, 'code, 'core]

public unparse
    arg dummy;
    
    return "1";
.

public test
    arg [args];
    
    return 1;
.



parent $logic_frob
object $false

var $root trusted []
var $root inited 1
var $root objname 'false
var $root created_on 796268969
var $root flags ['parameters, 'methods, 'code, 'core]

public unparse
    arg dummy;
    
    return "0";
.

public test
    arg [args];
    
    return 0;
.



parent $realms_frob
object $realm_of_creation

var $root inited 1
var $root objname 'realm_of_creation
var $root created_on 796268969
var $root flags ['parameters, 'methods, 'code, 'core]
var $named name ['prop, "<Creation>", "<Creation>"]
var $realms_frob local #[]



parent $ctext_tag
object $ctext_generator

var $root objname 'ctext_generator
var $root created_on 806278855
var $root inited 1
var $root flags ['parameters, 'methods, 'code, 'core]

public new
    arg name, flags, args;
    
    return pass(name, flags, args, tosym("gen_" + name));
.

public method
    arg self;
    
    if (type(self) == 'dictionary)
        return tosym("gen_" + self['name]);
    else
        return pass(self);
.



parent $ctext_tag
object $ctext_format

var $root objname 'ctext_format
var $root created_on 806278844
var $root inited 1
var $root flags ['parameters, 'methods, 'code, 'core]

public end
    arg self;
    
    if (type(self) == 'dictionary) {
        if (self['name][1] == "-")
            return 1;
        else
            return 0;
    } else if (self[1][1] == "-") {
        return 1;
    } else {
        return 0;
    }
.

public method
    arg self;
    
    if (type(self) == 'dictionary)
        return tosym("do_" + self['name]);
    else
        return pass(self);
.

public new
    arg name, flags, args;
    
    return pass(name, flags, args, tosym("do_" + name));
.



parent $thing_frob
object $wearable_frob

var $root child_index 1
var $root fertile 1
var $root inited 1
var $root objname 'wearable_frob
var $root created_on 796268969
var $root flags ['parameters, 'methods, 'code, 'fertile, 'core]
var $described prose []
var $named name ['normal, "Generic Wearable Frob", "a Generic Wearable Frob"]

public new
    arg [args];
    var name, prose;
    
    name = [@args, ["", 'none, []]][1];
    prose = [@args, [], []][2];
    if (type(name) != 'list)
        name = (> $code_lib.parse_name(name) <);
    if (type(prose) != 'list)
        throw(~type, "Prose must be submitted as a list.");
    return (<this(), #[['name, name], ['prose, prose]]>);
.

public wear
    arg dict;
    
    sender().wear(.to_frob(dict));
.



parent $user_interfaces
object $help_editing_ui

var $root objname 'help_editing_ui
var $root fertile 1
var $root created_on 805936681
var $root inited 1
var $root flags ['parameters, 'methods, 'code, 'fertile, 'core]
var $has_commands shortcuts #[]
var $has_commands local #[["@hl?ist|@help-list", [["@hl?ist|@help-list", "*", "@hl?ist|@help-list <any>", 'help_list_cmd, #[[1, ['any, []]]]]]], ["@hwrite|@help-write", [["@hwrite|@help-write", "*", "@hwrite|@help-write <any>", 'help_write_cmd, #[[1, ['any, []]]]]]]]
var $command_cache shortcut_cache 0
var $command_cache remote_cache 0
var $command_cache local_cache 0

protected help_write_cmd
    arg cmdstr, cmd, str;
    var node, text, errors;
    
    if (!str)
        node = .current_node();
    else
        node = .match_env_nice(str);
    if (!node.is($help_node))
        return node.namef('ref) + " is not a descendant of $help_node.";
    if (!node.is_writable_by(this()))
        return "You cannot write help on " + node.name() + "!";
    text = .read("-- Enter CML text for " + node.namef('ref) + " --");
    if (text == 'aborted)
        return;
    node.set_body(text);
    return "New help text set for " + node.namef('ref) + ".";
.

protected help_list_cmd
    arg cmdstr, cmd, str;
    var node, out;
    
    if (!str)
        node = .current_node();
    else
        node = .match_env_nice(str);
    if (!node.is($help_node))
        return node.namef('ref) + " is not a descendant of $help_node.";
    return ["@hwrite " + node.objname()] + node.body().uncompile() + ["."];
.



parent $user_interfaces
object $tree_ui

var $root inited 1
var $root objname 'tree_ui
var $root created_on 796268969
var $root flags ['parameters, 'methods, 'code, 'core]
var $has_commands local #[["@tparents", [["@tparents", "* tree *", "@tparents <any> tree <any>", 'parents_by_tree_cmd, #[[1, ['any, []]], [3, ['any, []]]]], ["@tparents", "*", "@tparents <any>", 'parents_by_tree_cmd, #[[1, ['any, []]]]]]], ["@tkids|@tchildren", [["@tkids|@tchildren", "* tree *", "@tkids|@tchildren <any> tree <any>", 'children_by_tree_cmd, #[[1, ['any, []]], [3, ['any, []]]]], ["@tkids|@tchildren", "*", "@tkids|@tchildren <any>", 'children_by_tree_cmd, #[[1, ['any, []]]]]]]]
var $command_cache shortcut_cache 0
var $command_cache remote_cache 0
var $command_cache local_cache 0

public parents_by_tree_cmd
    arg cmdstr, com, obj, [tree];
    var parents, line, par, colx, col, x;
    
    if (!tree)
        tree = "tree inheritance";
    tree = tosym(tree.to_list(" ").subrange(2).to_string());
    obj = .match_env_nice(obj);
    if (tree == 'inheritance)
        parents = obj.parents();
    else
        parents = obj.parents_by_tree(tree);
    col = .linelen() / 8;
    colx = col * 3;
    line = "Parent" + (parents.length() > 1 ? "s" | "");
    .tell(line + " of " + obj.namef('ref) + " in tree " + tostr(tree) + ":");
    line = " Name".pad(colx + 2) + " Perms".pad(col - 2);
    .tell(line + "Size ".pad(-col) + "Manager");
    for par in (parents) {
        line = " " + par.namef('xref);
        line = line.pad(colx + 2);
        line = line + (" " + $object_lib.see_perms(par)).pad(col - 2);
        line = line + (tostr(par.size()) + " ").pad(-col);
        line = line + $object_lib.get_name(par.manager(), 'namef, ['xref]).pad(colx);
    }
    .tell(line);
.

public children_by_tree_cmd
    arg cmdstr, com, obj, [tree];
    var children, line, child, colx, col, x;
    
    tree = [@tree, "", "inheritance"][2];
    tree = tosym(tree);
    obj = .match_env_nice(obj);
    if (tree == 'inheritance) {
        children = obj.children();
    } else {
        catch ~treenf {
            children = obj.children_by_tree(tree);
        } with handler {
            children = [];
        }
    }
    if (!children) {
        .tell("Children of " + obj.namef('xref) + "in tree " + tostr(tree) + ": ** None **");
    } else {
        col = .linelen() / 8;
        colx = col * 3;
        line = "Children" + (children.length() > 1 ? "s" | "");
        .tell(line + " of " + obj.namef('ref) + " in tree " + tostr(tree) + ":");
        line = " Name".pad(colx + 2) + " Perms".pad(col - 2);
        .tell(line + "Size ".pad(-col) + "Manager");
        for child in (children) {
            line = " " + child.namef('xref);
            line = line.pad(colx + 2);
            line = line + (" " + $object_lib.see_perms(child)).pad(col - 2);
            line = line + (tostr(child.size()) + " ").pad(-col);
            line = line + $object_lib.get_name(child.manager(), 'namef, ['xref]).pad(colx);
            .tell(line);
        }
    }
.


parent $user_interfaces
object $editor

var $root objname 'editor
var $root created_on 807225689
var $root inited 1
var $root flags ['parameters, 'methods, 'code, 'core]
var $has_commands shortcuts #[]
var $has_commands remote #[]
var $has_commands local #[["@e-abort", [["@e-abort", "", "@e-abort", 'abort_cmd, #[]]]], ["@e-insert", [["@e-insert", "*", "@e-insert <any>", 'insert_cmd, #[[1, ['any, []]]]]]], ["@e-delete", [["@e-delete", "*", "@e-delete <any>", 'delete_cmd, #[[1, ['any, []]]]]]], ["@e-save", [["@e-save", "", "@e-save", 'save_cmd, #[]]]], ["@e-line", [["@e-line", "*", "@e-line <number>", 'line_cmd, #[[1, ['number, []]]]]]], ["@e-list", [["@e-list", "*", "@e-list <any>", 'elist_cmd, #[[1, ['any, []]]]]]], ["@e-append", [["@e-append", "*", "@e-append <any>", 'eappend_cmd, #[[1, ['any, []]]]]]]]
var $command_cache shortcut_cache 0
var $command_cache remote_cache 0
var $command_cache local_cache 0
var $editor sender 0
var $editor finisher 0
var $editor text 0
var $editor line 0
var $editor modified 0

public startup
    arg finish_method, initial_text;
    
    (> .perms(sender(), 'this) <);
    if (modified)
        throw(~lock, "Editor is being used. Do a @save or @abort.");
    if (type(initial_text) == 'string)
        initial_text = [initial_text];
    sender = sender();
    finisher = finish_method;
    text = initial_text;
    line = 1;
    modified = 0;
.

public abort_cmd
    arg [args];
    
    (> .perms(sender(), 'this) <);
    clear_var('sender);
    clear_var('finisher);
    clear_var('text);
    clear_var('line);
    clear_var('modified);
    .tell("The editor is cleared.");
.

public insert_cmd
    arg cmdstr, cmd, what;
    
    (> .perms(sender(), 'this) <);
    modified = 1;
    text = text.insert(line, what);
    .tell("Line " + tostr(line) + " added.");
    line = line + 1;
.

public save_cmd
    arg [args];
    
    (> .perms(sender(), 'this) <);
    if ((> sender.(finisher)(text) <) == 'clear) {
        clear_var('sender);
        clear_var('finisher);
        clear_var('text);
        clear_var('line);
        clear_var('modified);
        .tell("Done. Editor cleared.");
    } else {
        .tell("Save completed.");
    }
.

public delete_cmd
    arg cmdstr, cmd, what;
    var start, end;
    
    (> .perms(sender(), 'this) <);
    catch any {
        start = ._parse_range(what);
    } with handler {
        .tell("Illegal range, can't delete.");
        return;
    }
    end = start[2];
    start = start[1];
    text = text.subrange(1, start - 1) + text.subrange(end + 1);
    modified = 1;
    line = start;
    .tell("Deleted " + tostr(end - start + 1) + (end == start ? " line." | " lines.") + " Current set to " + tostr(line) + ".");
.

public line_cmd
    arg cmdstr, cmd, number;
    
    (> .perms(sender(), 'this) <);
    if (number < 1 || number > text.length() + 1) {
        .tell("Out if range.");
        return;
    } else {
        line = number;
    }
    .tell("Current set to " + tostr(line));
.

public elist_cmd
    arg cmdstr, cmd, rangestr;
    var start, end, out, i, j, k, lineno;
    
    (> .perms(sender(), 'this) <);
    if (text == []) {
        .tell("There is no text.");
        return;
    }
    catch any {
        start = ._parse_range(rangestr);
    } with handler {
        .tell("Illegal range, can't list that.");
        return;
    }
    end = start[2];
    start = start[1];
    out = [];
    j = start;
    for i in (text.subrange(start, end - start + 1)) {
        lineno = tostr(j).right(5, j == line ? "^" | " ") + ": ";
        out = [@out, (lineno + i).wrap_line(.linelen(), "         ")];
        j = j + 1;
    }
    .tell(out);
.

public _parse_range
    arg what;
    var start, end, range;
    
    what = what.replace("$", tostr(text.length()));
    if (!what) {
        start = end = line > 1 ? line - 1 | 1;
    } else {
        range = (> $parse_lib.range(what) <);
        start = range[1];
        end = range[2] == 'single ? start | range[2];
    }
    if (start < 1 || end > text.length() || end < start)
        throw(~range, "Illegal range.");
    return [start, end];
.

public eappend_cmd
    arg cmdstr, cmd, what;
    
    (> .perms(sender(), 'this) <);
    modified = 1;
    if (line == 1) {
        text = text.insert(line, what);
        .tell("Line " + tostr(line) + " added.");
        line = line + 1;
    } else {
        text = text.replace(line - 1, what + text[line - 1]);
        .tell("Appended to line " + tostr(line - 1) + ".");
    }
.



parent $user_interfaces
object $set_ui

var $root fertile 1
var $root inited 1
var $root objname 'set_ui
var $root created_on 796268969
var $root flags ['parameters, 'methods, 'code, 'fertile, 'core]
var $has_commands local #[["@set", [["@set", "*", "@set <any>", 'set_cmd, #[[1, ['any, []]]]]]], ["@add-setting", [["@add-setting", "*", "@add-setting <any>", 'add_setting_cmd, #[[1, ['any, []]]]]]], ["@del-setting", [["@del-setting", "* on|from *", "@del-setting <any> on|from <any>", 'del_setting_cmd, #[[1, ['any, []]], [3, ['any, []]]]]]], ["@set-info", [["@set-info", "* on *", "@set-info <any> on <any>", 'settings_info_cmd, #[[1, ['any, []]], [3, ['any, []]]]]]]]
var $command_cache shortcut_cache 0
var $command_cache remote_cache 0
var $command_cache local_cache 0

public set_cmd
    arg set, name;
    var pos, obj;
    
    pos = name.rindex(" on ");
    if (pos) {
        obj = .match_env_nice(name.subrange(pos + 4));
        name = name.subrange(1, pos - 1);
    } else {
        obj = this();
    }
    return ._set_cmd(obj, name);
.

public add_setting_cmd
    arg cmdstr, com, name;
    var obj, pos, values, key, old_vals, line;
    
    pos = " on " in name;
    if (pos) {
        obj = .match_env_nics(name.subrange(pos + 4));
        name = name.subrange(pos - 1);
    } else {
        obj = this();
    }
    pos = " " in name;
    if (pos) {
        name = $string.trim(name.subrange(1, pos));
        line = name.subrange(pos);
    } else {
        line = "";
    }
    values = (| obj.get_setting_info(name) |) || $has_settings.default_setting_info();
    if (line) {
        values = values.add('check, ['value, tostr(values['check])]);
        values = values.add('get, ['value, tostr(values['get])]);
        values = values.add('set, ['value, tostr(values['set])]);
        values = values.add('del, ['value, tostr(values['del])]);
        values = values.add('type, ['value, values['type]]);
        old_vals = #[];
        old_vals = old_vals.add('set_args, values['set_args]);
        old_vals = old_vals.add('get_args, values['get_args]);
        values = $parse_lib.my_options(line, values);
        line = values[1];
        values = values[2];
        values = values.add('check, tosym(values['check]));
        values = values.add('get, tosym(values['get]));
        values = values.add('set, tosym(values['set]));
        values = values.add('del, tosym(values['del]));
        values = values.add('get_args, old_vals['get_args]);
        values = values.add('set_args, old_vals['set_args]);
    }
    if (obj.add_setting(name, values))
        .tell("Setting added.");
    else
        .tell("Usage: @add-setting <name> <options");
.

public _set_cmd
    arg obj, name;
    var pos, style, value, obj, definer, output, m, matches, info, args, n, o;
    
    // The method to do all the actual work for set_cmd and set_on_cmd
    if (!name)
        name = " ";
    
    //Find out just what the user wants to do.
    switch (name[1]) {
        case "+":
            style = 'boolean;
            value = 1;
            name = name.subrange(2);
        case "-":
            style = 'boolean;
            value = 0;
            name = name.subrange(2);
        default:
            pos = "=" in name;
            if (pos) {
                switch (name[pos - 1]) {
                    case "+":
                        style = 'add;
                        value = name.subrange(pos + 1);
                        name = name.subrange(1, pos - 2);
                    case "-":
                        style = 'del;
                        value = name.subrange(pos + 1);
                        name = name.subrange(1, pos - 2);
                    default:
                        style = 'set;
                        value = name.subrange(pos + 1);
                        name = name.subrange(1, pos - 1);
                }
            } else {
                style = 'get;
                value = "";
            }
    }
    pos = ":" in name;
    if (pos > 1) {
        definer = name.subrange(1, pos - 1);
        name = name.subrange(pos + 1);
    } else if (pos == 1) {
        definer = 0;
        name = name.subrange(2);
    } else {
        definer = 0;
    }
    
    // We think we know what the definer is. 
    // Make sure it's in the settings.
    // If not, add that string back on to the name
    if (definer && !obj.has_ancestor($object_lib.to_dbref(definer))) {
        name = definer + ":" + name;
        definer = 0;
    }
    
    // style may be 'boolean, 'set, 'add, 'del, 'get
    // 'get means we are looking at settings. 
    // others mean we want to change settings. 
    // If we are changing the a setting 'value is what we want to change it to.
    // name will contain whatever is left of the name.
    // Something like:
    //  help-node.evaluator
    //  terminated-tell
    //  .foo 
    // The stuff before the first '.' may be the objname of the definer.
    //   If it is, that string will be in settings.keys()
    // If the style is 'get, 
    //  we want to show the names and values of all settings that match name.
    if (style == 'get) {
        output = ["Settings on " + obj.name() + ":"];
        matches = obj.all_matching_settings(name, [], 1);
    
        //  .debug(matches);
        for m in (matches) {
            o = m[1];
            output = [@output, o.objname() + ":"];
            for n in (m[2]) {
                info = m[1].get_setting_info(n);
                output = [@output, "  " + n + " (" + info['type] + "): " + $data_lib.unparse(obj.get_setting(n, o))];
            }
        }
        .tell(output);
        return;
    }
    
    // After all that, we know 
    //  what the definer is, 
    //  the actual name of the setting,
    //  The value we wnat to set it to
    //  The style of change we are makeing
    catch ~definernf {
        if (!.set_setting(name, definer, value, style)) {
            .tell("! That is an invalid entry for that setting.");
            return;
        }
        .tell("Setting set");
    } with handler {
        .tell("Setting " + name + " not found.");
        return;
    }
.

public set_on_cmd
    arg set, name, on, obj;
    
    .debug("foo");
    return ._set_cmd(.match_env_nice(obj), name);
.

public set_info_cmd
    arg cmdstr, com, setting, on, definer;
    var pos, part, values, value, v, output;
    
    pos = " is " in definer;
    if (pos) {
        value = definer.subrange(pos + 4);
        definer = .match_env_nice(definer.subrange(1, pos - 1));
        setting = setting.to_list(":");
        part = tosym(setting[2]);
        setting = setting[1];
        switch (part) {
            case "set_args", "get_args":
                values = value.to_list(",");
                value = [];
                for v in (values) {
                    v = v.trim();
                    if (v[1] == "'")
                        v = tosym(v);
                    value = [@value, v];
                }
            case "check", "get", "set", "del":
                value = tosym(value);
            case "type":
                value = value;
            default:
                .tell("The setting part must be on of set_args, get_args, check, get, set, del, type");
        }
        part = tosym(part);
        definer.add_setting(setting, #[[part, value]]);
        .tell("Setting info changed.");
    } else {
        output = ["Setting " + setting + " on " + definer + ":"];
        definer = .match_env_nice(definer);
        values = .get_setting_info(setting);
        for value in (values.keys())
            output = [@output, "  " + tostr(value) + " = " + $data_lib.unparse(values[value])];
        .tell(output);
    }
.

public settings_info_cmd
    arg cmdstr, com, setting, on, definer;
    var pos, part, values, value, v, output;
    
    pos = " is " in definer;
    if (pos) {
        value = definer.subrange(pos + 4);
        definer = .match_env_nice(definer.subrange(1, pos - 1));
        setting = setting.to_list(":");
        part = tosym(setting[2]);
        setting = setting[1];
        switch (part) {
            case 'set_args, 'get_args:
                values = value.to_list(",");
                value = [];
                for v in (values) {
                    v = v.trim();
                    if (v[1] == "'")
                        v = tosym(v);
                    value = [@value, v];
                }
            case 'check, 'get, 'set, 'del:
                value = tosym(value);
            case 'type:
                value = value;
            default:
                .tell("The setting part must be on of set_args, get_args, check, get, set, del, type");
        }
        definer.add_setting(setting, #[[part, value]]);
        .tell("Setting info changed.");
    } else {
        output = ["Setting " + setting + " on " + definer + ":"];
        definer = .match_env_nice(definer);
        if (!definer.defines_setting(setting))
            .tell("Object does not define that setting.");
        values = definer.get_setting_info(setting);
        for value in (values.keys())
            output = [@output, "  " + tostr(value) + ": " + $data_lib.unparse(values[value])];
        .tell(output);
    }
.

public del_setting_cmd
    arg cmdstr, com, setting, on, obj;
    
    obj = .match_env_nice(obj);
    obj.del_setting(setting);
    .tell("Setting deleted.");
.



parent $editors
object $generic_editor

var $root inited 1
var $root objname 'generic_editor
var $root created_on 796268969
var $root flags ['parameters, 'methods, 'code, 'core]
var $command_aliases command_aliases []
var $generic_editor pass_through 0
var $has_commands local #[["@abort|abort", [["@abort|abort", "", "@abort|abort", 'abort_cmd, #[]]]], ["quit|@quit", [["quit|@quit", "", "quit|@quit", 'quit_cmd, #[]]]], ["list|nlist", [["list|nlist", "*", "list|nlist <any>", 'list_cmd, #[[1, ['any, []]]]]]], ["ins?ert", [["ins?ert", "*", "ins?ert <any>", 'insert_cmd, #[[1, ['any, []]]]]]], ["del?ete", [["del?ete", "*", "del?ete <any>", 'del_line_cmd, #[[1, ['any, []]]]]]], ["join", [["join", "*", "join <any>", 'join_cmd, #[[1, ['any, []]]]]]], ["enter", [["enter", "", "enter", 'enter_cmd, #[]]]], ["help", [["help", "", "help", 'help_screen, #[]]]]]
var $has_commands shortcuts #[["\"*", ['add_line_cmd, [1]]], ["s/*/*", ['sub_cmd, ["", 1, 2]]], ["fill * @*", ['fill_cmd, ["", 1, 2]]], ["fill *", ['fill_cmd, ["", 1, ""]]]]
var $command_cache shortcut_cache 0
var $command_cache remote_cache 0
var $command_cache local_cache 0

public start_editing
    arg what;
    
    // called by user.edit_cmd
    sender().tell(["The selected editor, " + .namef('id) + "must define .start_editing()."]);
    return 0;
.

public parse_command
    arg str;
    var cmd, loc, result, i, j, templates, template, word, fields, obj, verb_info, subbed_str;
    
    // This method works like $user.parse_command
    // The difference is that the permissions have changed, and after checking aliases and
    // commands, and nothing was found, resorting to sender().match_command() is an option.
    if (sender().editor() != this() || caller() != $user_input)
        throw(~perm, "Invalid access to parse_command.");
    
    // remove leading spaces, abort on blank line
    subbed_str = str;
    while (subbed_str && subbed_str[1] == " ")
        subbed_str = subbed_str.subrange(2);
    if (!subbed_str)
        return sender().editing();
    
    // sub the string through command aliases first
    subbed_str = .match_command_aliases(subbed_str);
    
    // Check commands on this.
    cmd = .match_command(subbed_str);
    if (cmd)
        return .(cmd[1])(sender().editing(), @cmd[2]);
    
    // if we're passing unknowns on to user.parse_command()...
    if (pass_through) {
        sender().tell("(Still editing.)");
        sender().run_with_users_perms(sender(), 'parse_command, str);
    } else {
        sender().tell("I don't understand that.");
    }
    return sender().editing();
.

public abort_cmd
    arg editing, com;
    
    (> .perms(sender(), 'this) <);
    if (type(editing) == 'dict) {
        if (editing['changed])
            editing['user].tell("Threw away changes and cleared editor variables.");
        else
            editing['user].tell("No changes to throw away. Cleared editor variables.");
    } else {
        (| $admin_2.tell(.namef('id) + ": editing corrupeted, == " + $data_lib.unparse(editing)) |);
    }
    return 0;
.

public quit_cmd
    arg editing, com;
    
    (> .perms(sender(), 'this) <);
    if (editing['changed]) {
        editing['user].tell("Changes have been made. Save first, or abort to cancel.");
        return editing;
    } else {
        editing['user].tell("Quiting editor.");
        return 0;
    }
.

public adjust
    arg text, [how];
    var line;
    
    for line in [1 .. text.length()] {
        if ('spaces in how)
            text = text.replace(line, " " + text[line]);
        if ('numbers in how)
            text = text.replace(line, $string.right(tostr(line), 3) + " :" + text[line]);
    }
    return text;
.

public list_cmd
    arg editing, com, rest;
    var line, text;
    
    (> .perms(sender(), 'this) <);
    if (com == "list") {
        editing['user].tell(editing['text]);
    } else {
        text = $generic_editor.adjust(editing['text], 'numbers);
        for line in [1 .. text.length()] {
            if (line == editing['cur_line] - 1)
                editing['user].tell(strsub($string.right(tostr(line), 3), " ", "_") + "_:" + editing['text][line]);
            else if (line == editing['cur_line])
                editing['user].tell(strsub($string.right(tostr(line), 3), " ", "^") + "^:" + editing['text][line]);
            else
                editing['user].tell(text[line]);
        }
    }
    return editing;
.

public insert_cmd
    arg editing, com, [args];
    var line, text;
    
    // re-position and (eventually/optionally) add text
    (> .perms(sender(), 'this) <);
    line = [@args, ""][1];
    text = [@args, "", ""][2];
    if (!line) {
        editing['user].tell(["Syntax: ins*ert line [text]", "!   You must supply a line number."]);
        return editing;
    }
    if (line != "$")
        line = toint(line);
    else
        line = editing['text].length() + 1;
    if (!line) {
        editing['user].tell(["Syntax: ins*ert line [text]", "!   You must supply a line number greater than or equal to 1."]);
        return editing;
    }
    if (line > editing['text].length() + 1)
        line = editing['text].length() + 1;
    editing = editing.replace('cur_line, line);
    ._show_line(editing);
    return editing;
.

public _show_line
    arg editing;
    var line;
    
    line = editing['cur_line] - 1;
    if (line)
        editing['user].tell(strsub($string.right(tostr(line), 3), " ", "_") + "_:" + editing['text][line]);
    else
        editing['user].tell("____");
    line = line + 1;
    if (line <= editing['text].length())
        editing['user].tell(strsub($string.right(tostr(line), 3), " ", "^") + "^:" + editing['text][line]);
    else
        editing['user].tell("^^^^");
    return editing;
.

public insert_line
    arg editing, line;
    
    // insert line at editing['cur_line]
    // assumes editing['cur_line] is within 1 .. editing['text].length()
    .perms(sender(), 'this);
    editing = editing.replace('text, editing['text].insert(editing['cur_line], line));
    editing = editing.replace('cur_line, editing['cur_line] + 1);
    editing = editing.replace('changed, 1);
    return editing;
.

public add_line_cmd
    arg editing, line;
    
    // "*
    (> .perms(sender(), 'this) <);
    editing = .insert_line(editing, line);
    
    // ._show_line(editing);
    editing['user].tell("Added line " + tostr(editing['cur_line] - 1) + ".");
    return editing;
.

public delete_line
    arg editing;
    
    // delete line at editing['cur_line]
    // assumes editing['cur_line] is within 1 .. editing['text].length()
    .perms(sender(), 'this);
    editing = editing.replace('text, editing['text].delete(editing['cur_line]));
    editing = editing.replace('changed, 1);
    editing = editing.replace('cur_line, $list.min(editing['cur_line], editing['text].length()));
    return editing;
.

public del_line_cmd
    arg editing, com, line;
    
    // del?ete line_num
    (> .perms(sender(), 'this) <);
    line = toint(line) || editing['cur_line];
    if (line <= editing['cur_line]) {
        editing = editing.replace('cur_line, line);
        editing['user].tell("Removed line " + tostr(editing['cur_line]) + ": \"" + editing['text][line] + "\".");
        editing = .delete_line(editing);
    } else {
        editing['user].tell("Line number out of range.");
    }
    
    //._show_line(editing);
    return editing;
.

public sub_cmd
    arg editing, com, was, is;
    
    // s/from/to/
    editing = editing.replace('text, editing['text].replace(editing['cur_line] - 1, editing['text][editing['cur_line] - 1].replace(was, is)));
    ._show_line(editing);
    return editing;
.

public fill_cmd
    arg editing, com, range, [col];
    var start, current, stop, new_text, idx;
    
    col = toint([@col, "75"][1]) || 75;
    if ("-" in range) {
        start = toint(range);
        if (!start) {
            editing['user].tell("Must supply a valid range, such as 10, 2-3, or 1-$.");
            return editing;
        }
        range = range.subrange(tostr(start).length() + 2);
        if (!range || range == "$")
            stop = editing['text].length();
        else
            stop = toint(range);
    } else {
        if (!range)
            start = editing['cur_line];
        else
            start = toint(range);
        if (!start) {
            editing['user].tell("Must supply a valid range, such as 10, 2-3, or 1-$.");
            return editing;
        }
        stop = start;
    }
    
    // actual change:
    new_text = editing['text];
    current = start;
    while (current <= stop) {
        if (new_text[current].length() > col) {
            idx = col;
            while (idx && new_text[current][idx] != " ")
                idx = idx - 1;
            if (!idx) {
                idx = col + 1;
                while (idx < new_text[current].length() && new_text[current][idx] != " ")
                    idx = idx + 1;
                if (idx == new_text[current].length())
                    idx = col;
            }
            new_text = new_text.insert(current + 1, new_text[current].subrange(idx + 1));
            new_text = new_text.replace(current, new_text[current].subrange(1, idx));
            stop = stop + 1;
        }
        current = current + 1;
    }
    editing = editing.replace('text, new_text);
    editing = editing.replace('cur_line, stop + 1);
    new_text = $generic_editor.adjust(editing['text], 'numbers);
    for current in [start .. stop - 1]
        editing['user].tell(new_text[current]);
    ._show_line(editing);
    return editing;
.

public join_cmd
    arg editing, com, range;
    var start, stop, current, new_text;
    
    new_text = editing['text];
    start = toint(range);
    range = range.subrange(tostr(start).length() + 2);
    stop = toint(range);
    if (range == "$")
        stop = new_text.length();
    if (!start || !stop) {
        editing['user].tell("Syntax: join <range>");
        editing['user].tell("!   You must supply a range of lines to join, such as 3-4 or 1-$");
        return editing;
    }
    stop = $list.min(stop, new_text.length());
    
    // actual change:
    while (start < stop) {
        new_text = new_text.replace(start, new_text[start] + new_text[start + 1]);
        new_text = new_text.delete(start + 1);
        stop = stop - 1;
    }
    editing = editing.replace('text, new_text);
    editing = editing.replace('cur_line, stop + 1);
    ._show_line(editing);
    return editing;
.

public enter_cmd
    arg editing, com;
    
    editing['user].read('done_entering, editing);
    return editing;
.

public done_entering
    arg text, editing;
    
    if (sender() != editing['user])
        throw(~perm, "Invalid access to done_entering.");
    if (text.length()) {
        if (text.length() > 1)
            editing['user].tell("Added lines " + tostr(editing['cur_line]) + " .. " + tostr(editing['cur_line] + text.length() - 1) + ".");
        else
            editing['user].tell("Added line " + tostr(editing['cur_line]) + ".");
        while (text) {
            editing = editing.replace('text, editing['text].insert(text[1], editing['cur_line]));
            editing = editing.replace('cur_line, editing['cur_line] + 1);
            text = test.delete(1);
        }
    }
.

public help_msg
    var msg;
    
    msg = [];
    msg = msg + ["  ins?ert *    set line number"];
    msg = msg + ["  del?ete *    delete line"];
    msg = msg + ["  \"*           insert text"];
    msg = msg + ["  abort        cancel changes and exit editor"];
    msg = msg + ["  quit         exit if no changes made since last save"];
    msg = msg + ["  list|nlist   list text without|with numbers"];
    msg = msg + ["  s/*/*        str sub: replace first with second in current line."];
    msg = msg + ["  fill * [@*]  wrap lines (@ col, def: 75)"];
    msg = msg + ["  join *       concatonates a range of lines"];
    return msg;
.

public help_screen
    arg editing, com;
    
    // l?ook
    .perms(sender(), 'this);
    editing['user].tell(.help_msg());
.



parent $builder
object $programmer

var $root owners [$programmer]
var $root inited 1
var $root owned [$programmer]
var $root manager $programmer
var $root quota 75000
var $root objname 'programmer
var $root created_on 796268969
var $root flags ['parameters, 'methods, 'code, 'core]
var $root managed [$programmer]
var $messaged verbs #[]
var $location contents []
var $located location $body_cave
var $located obvious 1
var $user password "*"
var $user connected_at 0
var $user last_command_at 0
var $user connections []
var $user modes #[]
var $user prompt ""
var $user parsers [$command_parser]
var $user tell_traceback ['brief, 0]
var $user context #[]
var $user filters []
var $programmer eval_prefix 0
var $programmer eval_tick_offset 0
var $programmer eval_offset 0
var $command_aliases command_aliases []
var $mail_list letters #[]
var $mail_list letters_index #[]
var $mail_list senders 1
var $mail_list readers []
var $mail_list notify [$programmer]
var $mail_list last_letter 0
var $mail_list mail []
var $mail_ui subscribed #[[$programmer, 791485891]]
var $mail_ui current #[['location, 0], ['list, $programmer]]
var $gendered gender $gender_neuter
var $described prose []
var $named name ['prop, "Generic Programmer", "Generic Programmer"]
var $named name_aliases []
var $user_data user_data #[['real_name, [1, "???"]], ['email, [1, "???"]]]
var $has_commands local #[["@show", [["@show", "*", "@show <any>", 'show_cmd, #[[1, ['any, []]]]]]], ["@program", [["@program", "*", "@program <any>", 'program_cmd, #[[1, ['any, []]]]]]], ["@d?isplay", [["@d?isplay", "*", "@d?isplay <any>", 'display_cmd, #[[1, ['any, []]]]]]], ["@dump", [["@dump", "*", "@dump <any>", 'dump_cmd, #[[1, ['any, []]]]]]], ["@id", [["@id", "*", "@id <any>", 'id_cmd, #[[1, ['any, []]]]]]], ["@which", [["@which", "*", "@which <any>", 'which_cmd, #[[1, ['any, []]]]]]], ["@mv|@move|@cp|@copy", [["@mv|@move|@cp|@copy", "*", "@mv|@move|@cp|@copy <any>", 'copy_move_cmd, #[[1, ['any, []]]]]]], ["@del-m?ethod|@delm?ethod|@dm", [["@del-m?ethod|@delm?ethod|@dm", "*", "@del-m?ethod|@delm?ethod|@dm <any>", 'del_method_cmd, #[[1, ['any, []]]]]]], ["@add-m?ethod|@addm?ethod|@am", [["@add-m?ethod|@addm?ethod|@am", "*", "@add-m?ethod|@addm?ethod|@am <any>", 'add_method_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, []]]]]]], ["@as", [["@as", "* eval *", "@as <any> eval <any>", 'eval_as_cmd, #[[1, ['any, []]], [3, ['any, []]]]]]], ["@def?iner", [["@def?iner", "* this|as * eval *", "@def?iner <any> this|as <any> eval <any>", 'eval_as_to_cmd, #[[1, ['any, []]], [3, ['any, []]], [5, ['any, []]]]]]], ["@join", [["@join", "*", "@join <any>", 'join_cmd, #[[1, ['any, []]]]]]], ["@ledit", [["@ledit", "*", "@ledit <any>", 'local_edit_cmd, #[[1, ['any, []]]]]]], ["@descend?ants", [["@descend?ants", "*", "@descend?ants <any>", 'descendants_cmd, #[[1, ['any, []]]]]]], ["@chpar?ents", [["@chpar?ents", "*", "@chpar?ents <any>", 'chparents_cmd, #[[1, ['any, []]]]]]], ["@del-p?arameter|@delp?arameter|@dp", [["@del-p?arameter|@delp?arameter|@dp", "*", "@del-p?arameter|@delp?arameter|@dp <any>", 'del_parameter_cmd, #[[1, ['any, []]]]]]], ["@add-p?arameter|@addp?arameter|@ap", [["@add-p?arameter|@addp?arameter|@ap", "*", "@add-p?arameter|@addp?arameter|@ap <any>", 'add_parameter_cmd, #[[1, ['any, []]]]]]], ["@chown", [["@chown", "*", "@chown <any>", 'chown_cmd, #[[1, ['any, []]]]]]], ["@add-parent|@addparent", [["@add-parent|@addparent", "*", "@add-parent|@addparent <any>", 'add_parent_cmd, #[[1, ['any, []]]]]]], ["@del-parent|@delparent", [["@del-parent|@delparent", "*", "@del-parent|@delparent <any>", 'del_parent_cmd, #[[1, ['any, []]]]]]], ["@add-owner|@addowner|@ao", [["@add-owner|@addowner|@ao", "*", "@add-owner|@addowner|@ao <any>", 'add_owner_cmd, #[[1, ['any, []]]]]]], ["@del-owner|@delowner|@do", [["@del-owner|@delowner|@do", "*", "@del-owner|@delowner|@do <any>", 'del_owner_cmd, #[[1, ['any, []]]]]]], ["@trace-method|@trace", [["@trace-method|@trace", "*", "@trace-method|@trace <object reference>", 'trace_method_cmd, #[[1, ['objref, []]]]]]], ["@teleport|@go", [["@teleport|@go", "*", "@teleport|@go <any>", 'teleport_cmd, #[[1, ['any, []]]]]]], ["@mmod|@method-mod", [["@mmod|@method-mod", "*", "@mmod|@method-mod <any>", 'mmod_cmd, #[[1, ['any, []]]]]]], ["@make-patch|@mp", [["@make-patch|@mp", "*", "@make-patch|@mp <any>", 'make_patch_cmd, #[[1, ['any, []]]]]]], ["@omod|@object-mod", [["@omod|@object-mod", "*", "@omod|@object-mod <any>", 'object_modify_cmd, #[[1, ['any, []]]]]]], ["@list", [["@list", "*", "@list <any>", 'list_cmd, #[[1, ['any, []]]]]]]]
var $has_commands shortcuts #[[";*", ['eval_cmd, ["eval", 1]]]]
var $help_ui indices [$help_index_root]
var $help_ui history [$help_summary]
var $help_ui current 1
var $has_messages message_info #[["teleport", #[]]]
var $has_messages messages #[[$programmer, #[["teleport.actor", <$ctext2_frob, [["You teleport to ", <$ctext_generator, ["dest", #[], #[], [], 'gen_dest]>, "."], #[['line, 1], ['this, $programmer]]]>], ["teleport.source", <$ctext2_frob, [[<$ctext_generator, ["actor", #[], #[], [], 'gen_actor]>, " teleports to ", <$ctext_generator, ["dest", #[], #[], [], 'gen_dest]>, "."], #[['line, 1], ['this, $programmer]]]>], ["teleport.dest", <$ctext2_frob, [[<$ctext_generator, ["actor", #[], #[], [], 'gen_actor]>, " teleports here from ", <$ctext_generator, ["source", #[], #[], [], 'gen_source]>, "."], #[['line, 1], ['this, $programmer]]]>]]]]
var $has_settings defined_settings #[["@program-options", #[['get, 'get_local_setting], ['set, 'set_local_setting], ['check, 'is_any], ['del, 'delete_local_setting], ['check_args, []], ['get_args, []], ['set_args, []]]], ["@list-options", #[['get, 'get_local_setting], ['set, 'set_local_setting], ['check, 'is_any], ['del, 'delete_local_setting], ['check_args, []], ['get_args, []], ['set_args, []]]], ["match-with", #[['get, 'get_local_setting], ['set, 'set_local_setting], ['check, 'check_match_with], ['del, 'delete_local_setting], ['check_args, []], ['get_args, []], ['set_args, []]]]]
var $has_settings local_settings #[]
var $has_settings settings #[]
var $command_cache shortcut_cache 0
var $command_cache remote_cache 0
var $command_cache local_cache 0

root init_programmer
    .set_tell_traceback('verbose, 4);
.

protected object_modify_cmd
    arg cmdstr, cmd, args;
    var opts, o, obj, out, name;
    
    opts = [["core"], ["f?ertile"], ["code"], ["p?arameters"], ["m?ethods"]];
    args = $parse_lib.getopt(args, opts);
    if (!args[1]) {
        out = [];
        for o in (opts.slice(1))
            out = [@out, "+|-" + o];
        out = out.prefix("      ");
        out = out.lcolumnize(.linelen() - 3);
        out = out.prefix("!  ");
        return ["=> Syntax: `" + cmd + " <object> [options]`", "!  Options:"] + out;
    }
    obj = .match_env_nice(args[1].to_string());
    if (!args[2])
        return ._display_object_header("", obj, 1);
    name = obj.name();
    for o in (args[2]) {
        catch any {
            if (!o[3]) {
                obj.del_flag(o[1].strip("?").to_symbol());
                .tell("Removing flag '" + o[1].strip("?") + " from " + name + ".");
            } else {
                obj.add_flag(o[1].strip("?").to_symbol());
                .tell("Adding flag '" + o[1].strip("?") + " to " + name + ".");
            }
        } with handler {
            .tell("Error: " + traceback()[1][2]);
        }
    }
.

protected eval_cmd
    arg cmdstr, com, str;
    var result, adjust, vars, v, evalp, ticks, time, mtime, line, offset;
    
    (> .perms(sender(), 'this) <);
    adjust = !str;
    if (!eval_offset) {
        adjust = 2;
        eval_offset = #[];
    }
    evalp = .eval_prefix();
    vars = "var " + evalp.keys().to_string(", ") + "; ";
    for v in (evalp.values())
        vars = vars + v + "; ";
    
    // perform escape substitution
    if (str && str[1] == "|")
        str = str.subrange(2);
    else
        str = .eval_subs(str);
    
    // Evaluate the line.
    if (str && str[1] == ";") {
        str = str.subrange(2);
        result = .eval([str]);
    } else {
        if (str) {
            while (str[str.length()] == ";")
                str = str.subrange(1, str.length() - 1);
        }
        str = vars + " return (> " + str + " <);";
        result = (> .evaluate(str) <);
        ticks = result[1][1];
        time = result[1][2];
        mtime = result[1][3];
        result = result[2];
        offset = .eval_offset();
        if (!adjust) {
            ticks = ticks - offset['ticks];
            time = time - offset['time];
            mtime = mtime - offset['mtime];
        }
    
        // figure out times/ticks
        if (adjust) {
            eval_offset = eval_offset.add('ticks, ticks);
            eval_offset = eval_offset.add('mtime, mtime);
            eval_offset = eval_offset.add('time, time);
            line = tostr(offset['ticks] - ticks) + " ticks and ";
            line = line + tostr(offset['time] - time) + ".";
            line = line + tostr(offset['mtime] - mtime) + " seconds.";
            .tell("Eval offset adjusted by " + line);
            if (adjust == 1)
                return;
        }
    }
    
    // Display the errors, or the result.
    if (result[1] == 'errors) {
        .tell(result[2]);
    } else {
        .tell("=> " + $data_lib.unparse(result[2], 'full));
        if (ticks) {
            line = "[ ticks: " + tostr(ticks) + " seconds: " + tostr(time);
            line = line + "." + tostr(mtime);
            if (time)
                line = line + " (" + tostr(ticks / time) + " ticks per second)";
            .tell(line + " ]");
        }
    }
.

protected program_cmd
    arg cmdstr, com, args;
    var syn, opts, edited, sref, line, code, errors;
    
    syn = com + " [obj].<method> [options...]";
    edited = "";
    args = args + ((| " " + .setting("@program-options") |) || "");
    opts = .parse_methodcmd_options(syn, args, [["e?dited"], ["c?omment", 1]], #[['edited, [1, ""]], ['comment, [0, ""]]]);
    if (opts['error]) {
        (| .tell_error(syn, opts['error]) |);
        opts = opts.add('ignore, 1);
    }
    if (!opts['edited][1] && !$sys.is_admin(this())) {
        .tell("Only admins can shut off edited comments.");
        opts = opts.add('edited, 0).add('ignore, 1);
    }
    if (opts['edited][1]) {
        edited = "// $#Edited: " + $time.format("%d %h %y %H:%M") + " " + .namef('ref);
        if (opts['comment][2])
            edited = edited + ": " + opts['comment][2];
    }
    if (opts['ignore]) {
        line = "Ignoring input until \".\" or \"@abort\"";
    } else {
        sref = opts['object].objname() + "." + tostr(opts['method]) + "()";
        if (opts['exists])
            line = "Reprogramming " + sref;
        else
            line = "Programming " + sref;
    }
    code = .read("-- " + line + " --");
    if (code == 'aborted)
        return;
    if (opts['ignore])
        return "Finished ignoring input.";
    if (edited)
        code = code + [edited];
    catch any {
        errors = opts['object].compile(code, opts['method]);
        if (errors)
            return errors;
        line = $code_lib.verify_code(code, opts['method]);
        opts['object].set_method_flags(opts['method], opts['mflags]);
        opts['object].set_method_state(opts['method], opts['mstate]);
        if (line)
            .tell(line);
        return "Method " + sref + " " + (opts['exists] ? "re" | "") + "compiled";
    } with handler {
        return traceback()[1][2];
    }
.

protected show_cmd
    arg cmdstr, com, args;
    var obj, opts, show, syn, wildcard;
    
    (> .perms(sender(), 'this) <);
    syn = com + " <object> [<wildcard>] [flags]";
    opts = #[["p", [1, 0]], ["m", [1, 0]], ["v", [0, 0]], ["c", [1, 0]]];
    args = $parse_lib.options(args, opts);
    opts = args[2];
    args = args[1];
    if (!args)
        $parse_lib.tell_error("", syn);
    show = [];
    if (opts["p"][1])
        show = ['parameters];
    if (opts["m"][1])
        show = [@show, 'methods];
    obj = .match_env_nice(args[1]);
    if (args.length() > 1)
        wildcard = args[2];
    else
        wildcard = "";
    catch ~perm {
        return obj.show(wildcard, opts["c"][1], opts["v"][1], @show);
    } with handler {
        .tell(traceback()[1][2]);
    }
.

protected _move_method
    arg syn, ref1, ref2, [opts];
    var line, code;
    
    .perms(sender(), 'this);
    catch any {
        ref1 = $parse_lib.reference(ref1);
        ref2 = $parse_lib.reference(ref2);
        if (!ref1[2] || !ref2[2])
            .tell_error(syn, "You must define both a method to be copied from, and one to be copied to.");
        ref1 = ref1.replace(1, .match_env_nice(ref1[1]));
        ref1 = ref1.replace(2, tosym(ref1[2]));
        ref2 = ref2.replace(1, .match_env_nice(ref2[1]));
        ref2 = ref2.replace(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].objname() + "." + 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].objname() + "." + tostr(ref1[2]) + " moved to ";
        line = line + ref2[1].objname() + "." + tostr(ref2[2]) + ".";
        .tell(line);
    } with handler {
        switch (error()) {
            case ~methodnf:
                line = traceback()[1][2];
                line = line.subrange(1, line.length() - 1);
                .tell_error(syn, line + " on " + ref1[1].namef('ref) + ".");
            case ~perm:
                .tell_error(syn, "You cannot write on that object.");
            case ~stop:
                rethrow(error());
            default:
                // they are tuff programmers, they can deal with the tracebacks
                .tell_traceback(traceback());
        }
    }
.

protected del_method_cmd
    arg cmdstr, com, what;
    var syntax, obj, ref, res;
    
    (> .perms(sender(), 'this) <);
    syntax = com + " <obj>.<method>()";
    what = what.explode();
    if (!what)
        $parse_lib.tell_error("You must give an object and a method to delete.", syntax);
    catch any {
        ref = $parse_lib.full_reference(what[1], this(), [this(), 'match_env_nice]);
        if (ref[1] != 'method || ref[3] == 0) {
            .tell("The object.method() reference \"" + what[1] + "\" is invalid.");
            return;
        }
        ref[2].del_method(ref[3]);
        .tell("Method " + ref[2].objname() + "." + tostr(ref[3]) + "() deleted.");
    } with handler {
        .tell(traceback()[1][2]);
    }
.

protected move_cmd
    arg cmdstr, com, [args];
    var syn;
    
    (> .perms(sender(), 'this) <);
    syn = com + " [from] <$obj.ref> [to] <$obj.ref>";
    
    // figure up args
    args = args[1].explode();
    if (args && args[1] == "from")
        args = args.delete(1);
    if (args.length() > 1 && args[2] == "to")
        args = args.delete(2);
    if (args.length() < 2)
        $parse_lib.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_lib.tell_error("You must define both methods, or both parameters.", syn);
.

protected list_cmd
    arg cmdstr, com, args;
    var nums, pattern, ref, methods, s, def, method, opts, str;
    
    s = com + " <object>.<method>";
    opts = (| " " + .setting("@list-options") |) || "";
    args = $parse_lib.getopt(args + opts, [["n?umbered"]]);
    nums = (| "n?umbered" in args[2].slice(1) |);
    opts = (| .setting("@program-options") |) || "";
    if (nums)
        nums = args[2][nums][3];
    str = args[1].to_string();
    catch any {
        ref = $parse_lib.xreference(str);
        if (ref[1] != 'method)
            (> .tell_error(s, "You must submit an object.method() reference") <);
        def = (| ref[2].find_method(ref[4]) |);
        if (!def) {
            if (ref[4] == 0)
                pattern = "*";
            else
                pattern = tostr(ref[4]);
            def = ref[2];
            methods = [];
            for method in (def.methods()) {
                if ($string.match_pattern(pattern, tostr(method)))
                    methods = methods + [method];
            }
        } else {
            pattern = tostr(ref[4]);
            methods = [ref[4]];
        }
    } with handler {
        switch (error()) {
            case ~methodnf:
                str = ref[2].objname() + "." + tostr(ref[4]) + "()";
                .tell_error(s, line + " not found.");
            case ~stop:
                return;
            default:
                .tell_error(s, traceback()[1][2]);
        }
        .tell_error(s, traceback()[1][2]);
    }
    if (!methods)
        return .tell("No method found matching \"" + pattern + "\".");
    for method in (methods)
        .tell(._list_method(def, method, nums, opts));
.

protected copy_move_cmd
    arg cmdstr, com, [args];
    var syn, how, line;
    
    (> .perms(sender(), 'this) <);
    syn = com + " [from] <$obj.ref> [to] <$obj.ref>";
    
    // figure up args
    args = args[1].explode();
    args = [@args, ""];
    args = args.setremove("from");
    args = args.setremove("to");
    if (args.length() != 3)
        $parse_lib.tell_error("Send two object references.", syn);
    how = com in ["@mv", "@move"] ? 'move | 'copy;
    catch ~namenf {
        args = [$parse_lib.full_reference(args[1], sender()), args[2], args[3]];
        args = [args[1], $parse_lib.full_reference(args[2], sender()), args[3]];
        if (args[1][1] != args[2][1] && args[2][1] != 'unknown)
            $parse_lib.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_lib.tell("You must specify a full reference for the source object.", syn);
    } with handler {
        $parse_lib.tell_error(traceback()[1][2], syn);
    }
.

protected id_cmd
    arg cmdstr, cmd, obj;
    
    (> .perms(sender(), 'this) <);
    obj = .match_env_nice(obj);
    .tell(obj.namef('xref) + " " + $object_lib.see_perms(obj) + " " + $data_lib.unparse(obj.parents()) + " " + tostr(obj.size()) + " bytes");
.

protected dump_cmd
    arg cmdstr, com, args;
    var opts, obj, objname, x, pobjname, 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_lib.options(args, opts);
    if (!args[1])
        $parse_lib.tell_error(@syn);
    obj = .match_env_nice(args[1][1]);
    me = this();
    objname = obj.objname();
    opts = args[2];
    readable = obj.flags();
    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 objname vs objnums
                for x in (obj.parents())
                    .tell("parent " + x.objname());
                .tell("object " + objname);
                .tell("");
            }
            for par in (data) {
                if (type(par[2]) == 'string) {
                    .tell(" *** " + par[1].objname() + "'s parameters are unreadable by you ***");
                } else {
                    pobjname = (| par[1].objname() |) || toliteral(par[1]);
                    for x in (par[2])
                        .tell("var " + pobjname + " " + tostr(x[1]) + " " + $data_lib.unparse(x[2]));
                }
            }
        }
    }
    if (opts["m"][1]) {
        .tell("");
        if (!('code in readable)) {
            .tell(" *** " + objname + "'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 " + objname + "." + tostr(x) + tail);
                else
                    .tell("method " + tostr(x));
                for x in [1 .. code.length()]
                    code = code.replace(x, "    " + code[x]);
                .tell(code);
                .tell([".", ""]);
    
                // this can get long
                pause();
            }
        }
    }
.

protected add_method_cmd
    arg cmdstr, com, what;
    var syntax, obj, ref, res;
    
    (> .perms(sender(), 'this) <);
    syntax = com + " <obj>.<method>()";
    what = what.explode();
    if (!what)
        $parse_lib.tell_error("You must give an object and a method to add.", syntax);
    catch any {
        ref = $parse_lib.full_reference(what[1], this(), [this(), 'match_env_nice]);
        if (ref[1] != 'method || ref[3] == 0) {
            .tell("The object.method() reference \"" + what[1] + "\" is invalid.");
            return;
        }
        if ((| ref[2].find_method(ref[3]) |)) {
            .tell("Method " + ref[2].objname() + "," + tostr(ref[3]) + " already exists!");
            return;
        }
        ref[2].compile([""], ref[3]);
        .tell("Method " + ref[2].objname() + "," + tostr(ref[3]) + " added.");
    } with handler {
        .tell(traceback()[1][2]);
    }
.

protected descendants_cmd
    arg cmdstr, cmd, args;
    var syn, obj, maxlevels, line;
    
    (> .perms(sender(), 'this) <);
    syn = cmd + " <obj> [<levels>]";
    args = args.explode();
    if (!(args.length() in [1, 2]))
        $parse_lib.tell_error("", 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 = "Descendants of " + obj.objname() + " [";
    line = line + obj.parents().map('objname).to_english() + "], ";
    if (maxlevels) {
        line = line + tostr(maxlevels - 1);
        line = line + " level" + (maxlevels - 1 > 1 ? "s" | "s") + ":";
    } else {
        line = line + "all levels:";
    }
    .tell(line);
    .tell(obj._display_descendants("", [], 0, maxlevels));
    .tell("---");
.

protected chparents_cmd
    arg cmdstr, cmd, args;
    var objs, syn, x, obj, parents;
    
    (> .perms(sender(), 'this) <);
    syn = cmd + " <child> [to] <parent>, <parent>, ...";
    if (!args)
        $parse_lib.tell_error("", syn);
    if (" to " in args) {
        if ("," in args)
            args = args.replace(" to ", ", ");
        else
            args = args.replace(" to ", " ");
    }
    objs = $object_lib.str_to_objlist(args);
    if (objs['invalid])
        .tell("Unable to find objects: " + objs['invalid].to_english());
    objs = objs['valid];
    if (!objs)
        return;
    if (objs.length() < 2)
        $parse_lib.tell_error("No parents defined.", syn);
    obj = objs[1];
    parents = objs.subrange(2);
    catch any {
        obj.chparents(@parents);
        .tell("Successfully changed parent list for " + obj.objname() + " to " + $data_lib.unparse(parents) + ".");
    } with handler {
        .tell(traceback()[1][2]);
    }
.

protected _display_object_header
    arg what, obj, chop;
    var len, line, out;
    
    (> .perms(sender(), 'this) <);
    len = .linelen();
    line = "Object:   " + obj.namef('xref) + " ";
    line = line + $object_lib.see_perms(obj);
    .tell(line.left(len / 3 * 2) + " Size: " + obj.size().to_english());
    .tell("Manager:  " + $object_lib.get_name(obj.manager(), 'namef, ['xref]));
    line = obj.writers();
    if (line.length() > 1)
        line = "Writers:  " + line.map('objname).to_english();
    else if (!line)
        line = "Writers:  (none)";
    else
        line = "Writer:   " + line[1].namef('xref);
    line = obj.owners();
    if (line.length() > 1)
        line = "Owners:   " + line.map('objname).to_english();
    else if (!line)
        line = "Owner:    (none)";
    else
        line = "Owner:    " + line[1].namef('xref);
    if (chop)
        line = line.chop(len);
    .tell(line);
    line = obj.parents();
    if (line.length() > 1)
        line = "Parents:  " + line.map('objname).to_english();
    else if (!line)
        line = "Parents:  (none)";
    else
        line = "Parent:   " + line[1].namef('xref);
    if (chop)
        line = line.chop(len);
    .tell(line);
    if (obj.has_ancestor($located))
        .tell("Location: " + $object_lib.get_name(obj.location(), 'namef, ['xref]));
.

protected _display_methods
    arg obj, info, chop, comments;
    var type, types, line, out, tidbit, len;
    
    (> .perms(sender(), 'this) <);
    len = .linelen();
    types = ["Public", "Protected", "Private", "Root", "Driver"];
    out = [];
    for type in [1 .. 5] {
        if (info[type]) {
            out = out + [types[type] + " Methods:"];
            for tidbit in (info[type]) {
                line = $object_lib.parse_method_flags(tidbit[4]) + tostr(tidbit[5]).right(4) + " " + (tidbit[1] != obj ? tidbit[1].objname() | "") + "." + tostr(tidbit[2]) + "(" + tidbit[3] + ")";
                if (comments && tidbit[6]) {
                    line = line.pad((len - 1) / 2);
                    if (tidbit[6][1] != "/")
                        line = line + " // " + tidbit[6];
                    else
                        line = line + " " + tidbit[6];
                }
                if (chop)
                    line = line.chop(len);
                out = out + [line];
                pause();
            }
        }
        pause();
    }
    .tell(out);
.

protected teleport_cmd
    arg cmdstr, 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 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_lib.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.");
.

protected info_cmd
    arg cmdstr, 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("-----");
.

protected 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 = 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 = 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 = idx + 1;
            sub = .match_env_nice(code.subrange(1, idx - 1));
            ret_code = ret_code + sub.objname();
            code = code.subrange(idx);
        }
    }
    return ret_code;
.

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

protected del_command_cmd
    arg cmdstr, cmd, str;
    var s, ref, t, args;
    
    (> .perms(sender(), 'this) <);
    
    // we have to do our own matching, because the parser would get the
    // template mixed up with our template
    s = cmd + " \"template\" [from] $object.method()";
    args = str.explode_quoted();
    if (args.length() < 2)
        .tell_error(s, "Unable to parse arguments \"" + str + "\".");
    t = args[1];
    if (!t)
        return "No template specified.";
    catch any {
        ref = $parse_lib.xreference(args[args.length()]);
        if (ref[1] != 'method)
            return toliteral(str) + " is an invalid method reference.";
        if (!(> ref[2].del_command(t, ref[4]) <))
            return "Command " + toliteral(t) + " is not defined on " + ref[2].objname() + ".";
    } with handler {
        return traceback()[1][2];
    }
    return "Removed command " + toliteral(t) + " associated with " + $parse_lib.buildref(@ref) + ".";
.

public _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 = matches + [[more, cmd[3], cmd[4]]];
        }
    }
    return matches;
.

protected which_cmd
    arg cmdstr, command, str;
    var m, c, l, t, p, s, cmds, def, out, dname, line, lcache, rcache;
    
    if (!str)
        .tell_error(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 = 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].objname() + ".";
        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 = out + [line];
        }
    }
    return out;
.

protected _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].objname() + "." + 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].objname() + "." + tostr(ref1[3]) + " ";
        line = line + (how == 'move ? "moved" | "copied") + " ";
        line = line + ref2[2].objname() + "." + tostr(ref2[3]) + ".";
        .tell(line);
    } with handler {
        switch (error()) {
            case ~methodnf:
                line = traceback()[1][2];
                line = line.subrange(1, line.length() - 1);
                $parse_lib.tell_error(line + " on " + ref1[1].namef('ref) + ".", syn);
            case ~perm:
                $parse_lib.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());
        }
    }
.

protected add_command_cmd
    arg cmdstr, cmd, str;
    var s, ref, t, args;
    
    (> .perms(sender(), 'this) <);
    
    // we have to do our own matching, because the parser would get the
    // template mixed up with our template
    s = cmd + " \"template\" [to|for] $object.method()";
    args = str.explode_quoted();
    if (args.length() < 2)
        .tell_error(s, "Unable to parse arguments \"" + str + "\".");
    t = args[1];
    if (!t)
        return "No template specified.";
    catch any {
        ref = $parse_lib.xreference(args[args.length()]);
        if (ref[1] != 'method)
            return toliteral(str) + " is an invalid method reference.";
        (> ref[2].add_command(t, ref[4]) <);
    } with handler {
        return traceback()[1][2];
    }
    return "Added command " + toliteral(t) + " associated with " + $parse_lib.buildref(@ref) + ".";
.

protected eval_as_cmd
    arg cmdstr, 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.objname() + " => " + $data_lib.unparse(result[2]));
.

protected eval_as_to_cmd
    arg cmdstr, 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_lib.unparse(result[2]));
.

protected teleport
    arg dest;
    var m, source, vars;
    
    (> .perms(sender(), 'this) <);
    source = .location();
    if (!(| .move_to(dest) |))
        return 0;
    .look_cmd();
    vars = #[["$actor", this()], ["actor", .name()], ["$source", source], ["source", source.name()], ["$dest", dest], ["dest", dest.name()]];
    m = .eval_message("teleport", vars, $programmer);
    dest.announce(m);
    source.announce(m);
.

protected join_cmd
    arg cmdstr, cmd, who;
    var loc, p, user;
    
    (> .perms(sender(), 'this) <);
    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.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() + ".");
.

protected local_edit_cmd
    arg cmdstr, cmd, args;
    var syn, ref, edited, why, epref, spref, line, code, ancestor;
    
    syn = cmd + " <obj>.<method> [-edited] [comments]";
    args = $parse_lib.options(args, #[["edited", [1, ""]]]);
    edited = args[2]["edited"][1];
    args = args[1];
    if (!args)
        .tell_error(syn, "No <object>.<method> reference specified.");
    catch any {
        ref = $parse_lib.full_reference(args[1], this(), [this(), 'match_environment]);
    } with handler {
        .tell_error(syn, traceback()[1][2]);
    }
    if (ref[1] != 'method)
        .tell_error(syn, "Invalid <object>.<method> reference.");
    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()))
        return .tell("You cannot program on that object.");
    why = args.subrange(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].objname() + "." + tostr(ref[3]) + "()";
                .tell_error(syn, line + " not found.");
            default:
                .tell_error(syn, traceback()[1][2]);
        }
    }
    .tell("#$# edit name: " + ref[2].name() + "." + tostr(ref[3]) + " upload: @program " + ancestor.objname() + "." + tostr(ref[3]));
    .tell(code);
    .tell(".");
.

protected display_cmd
    arg cmdstr, cmd, args;
    var opts, args, i, chop, filter, comments, gen, def, info, what, swhat, obj, syn, ref;
    
    (> .perms(sender(), 'this) <);
    syn = cmd + " <object reference> [<object>]";
    
    // parse args
    opts = [["c?hop", 0], ["f?ull", 0], ["g?eneration", 1], ["d?efiners", 1]];
    args = $parse_lib.getopt(args, opts);
    opts = args[2];
    args = args[1];
    chop = 1;
    gen = ['generations, 1];
    if (!(args.length() == 1))
        .tell_error(syn);
    catch any {
        ref = $parse_lib.xreference(args[1]);
        def = [ref[3]];
        for i in (opts) {
            switch (i[1]) {
                case "c?hop":
                    comments = i[3];
                case "f?ull":
                    comments = i[3];
                case "g?eneration":
                    gen = i[4];
                    if (gen.is_numeric())
                        gen = ['generations, toint(gen)];
                    else
                        gen = ['ancestors_to, .match_env_nice(gen)];
                case "d?efiners":
                    def = i[4].parse_english_list().omap(this(), 'match_env_nice).union(def).compress();
                default:
                    .tell_error(syn);
            }
        }
    } with handler {
        .tell_error(syn, traceback()[1][2]);
    }
    what = ref[1];
    swhat = tostr(ref[1]).capitalize();
    obj = ref[2];
    
    // display it
    ._display_object_header(what, obj, chop);
    if (what in ['method, 'variable]) {
        if (!tostr(ref[4]))
            filter = "";
        else
            filter = tostr(ref[4]);
        info = obj.(tosym(tostr(what) + "_info"))(gen, def, filter);
        if (info) {
            if (info.length() > 1)
                swhat = swhat + "s";
            .tell(swhat + " matching \"" + filter + "\":");
            .(tosym("_display_" + tostr(what) + "s"))(obj, info, chop, comments);
        }
    }
    return "+ finis +";
.

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

protected chown_cmd
    arg cmdstr, cmd, args;
    var objs, syn, x, obj, owners;
    
    (> .perms(sender(), 'this) <);
    syn = cmd + " <object> [to] <owner>, <owner>, ...";
    if (!args)
        .tell_error(syn);
    if (" to " in args) {
        if ("," in args)
            args = args.replace(" to ", ", ");
        else
            args = args.replace(" to ", " ");
    }
    objs = $object_lib.str_to_objlist(args);
    if (objs['invalid])
        .tell("Unable to find objects: " + objs['invalid].to_english());
    objs = objs['valid];
    if (!objs)
        return;
    if (objs.length() < 2)
        .tell_error(syn, "No owners defined.");
    obj = objs[1];
    owners = objs.subrange(2);
    catch any {
        obj.chown(@owners);
        .tell("Successfully changed owner list for " + obj.objname() + " to " + $data_lib.unparse(owners) + ".");
    } with handler {
        .tell(traceback()[1][2]);
    }
.

protected del_parameter_cmd
    arg cmdstr, cmd, what;
    var syntax, ref;
    
    (> .perms(sender(), 'this) <);
    syntax = cmd + " <obj>,<parameter>";
    what = what.explode();
    if (!what)
        .tell_error(syntax, "You must give an object and parameter to delete");
    catch ~paramnf, ~namenf, ~perm {
        ref = $parse_lib.full_reference(what[1], this(), [this(), 'match_env_nice]);
        if (ref[1] != 'parameter || ref[3] == 0) {
            .tell("The object,parameter reference \"" + what[1] + "\" is invalid.");
            return;
        }
        ref[2].del_parameter(ref[3]);
        .tell("Parameter " + ref[2].objname() + "," + tostr(ref[3]) + " deleted.");
    } with handler {
        .tell(traceback()[1][2]);
    }
.

protected add_parameter_cmd
    arg cmdstr, cmd, what;
    var syntax, ref, value;
    
    (> .perms(sender(), 'this) <);
    syntax = cmd + " <obj>,<parameter> [<value>]";
    what = what.explode();
    if (!what)
        .tell_error(syntax, "You must give an object and parameter to add");
    catch ~paramnf, ~namenf, ~perm {
        ref = $parse_lib.full_reference(what[1], this(), [this(), 'match_env_nice]);
        if (ref[1] != 'parameter || ref[3] == 0) {
            .tell("The object,parameter reference \"" + what[1] + "\" is invalid.");
            return;
        }
    
        // this is a cheap way to evaluate their set value
        if (what.length() > 1) {
            value = .eval(["return " + what.subrange(2).to_string() + ";"]);
            if (value[1] == 'errors) {
                .tell("Unable to parse value \"" + what.subrange(2).to_string() + "\", default value is \"0\".");
                value = 0;
            } else {
                value = value[2];
            }
        }
        ref[2].add_parameter(ref[3], value);
        .tell("Parameter " + ref[2].objname() + "," + tostr(ref[3]) + " added" + (value != 0 ? " with value " + toliteral(value) + "." | "."));
    } with handler {
        .tell(traceback()[1][2]);
    }
.

protected add_parent_cmd
    arg cmdstr, cmd, args;
    var syn, obj, parent;
    
    (> .perms(sender(), 'this) <);
    syn = cmd + " <parent> [to] <object>";
    if (!args)
        .tell_error(syn);
    if (" to " in args)
        args = args.replace(" to ", " ");
    args = args.explode();
    if (args.length() != 2)
        .tell_error(syn, "Invalid number of arguments.");
    parent = .match_env_nice(args[1]);
    obj = .match_env_nice(args[2]);
    catch any {
        obj.add_parent(parent);
        .tell("Sucessfully added parent to " + obj.objname() + ", new parent list: " + obj.parents().map_to_english('objname) + ".");
    } with handler {
        .tell(traceback()[1][2]);
    }
.

protected del_parent_cmd
    arg cmdstr, cmd, args;
    var syn, obj, parent;
    
    (> .perms(sender(), 'this) <);
    syn = cmd + " <parent> [from] <object>";
    if (!args)
        .tell_error(syn);
    if (" from " in args)
        args = args.replace(" from ", " ");
    args = args.explode();
    if (args.length() != 2)
        .tell_error(syn, "Invalid number of arguments.");
    parent = .match_env_nice(args[1]);
    obj = .match_env_nice(args[2]);
    catch any {
        obj.del_parent(parent);
        .tell("Sucessfully removed parent from " + obj.objname() + ", new parent list: " + obj.parents().map_to_english('objname) + ".");
    } with handler {
        .tell(traceback()[1][2]);
    }
.

protected add_owner_cmd
    arg cmdstr, cmd, args;
    var syn, obj, owner;
    
    (> .perms(sender(), 'this) <);
    syn = cmd + " <owner> [to] <object>";
    if (!args)
        .tell_error(syn);
    if (" to " in args)
        args = args.replace(" to ", " ");
    args = args.explode();
    if (args.length() != 2)
        .tell_error(syn, "Invalid number of arguments.");
    owner = .match_env_nice(args[1]);
    obj = .match_env_nice(args[2]);
    catch any {
        obj.add_owner(owner);
        .tell("Sucessfully added owner to " + obj.objname() + ", new owner list: " + obj.owners().map_to_english('objname) + ".");
    } with handler {
        .tell(traceback()[1][2]);
    }
.

protected del_owner_cmd
    arg cmdstr, cmd, args;
    var syn, obj, owner;
    
    (> .perms(sender(), 'this) <);
    syn = cmd + " <owner> [from] <object>";
    if (!args)
        .tell_error(syn);
    if (" from " in args)
        args = args.replace(" from ", " ");
    args = args.explode();
    if (args.length() != 2)
        .tell_error(syn, "Invalid number of arguments.");
    owner = .match_env_nice(args[1]);
    obj = .match_env_nice(args[2]);
    catch any {
        obj.del_owner(owner);
        .tell("Sucessfully removed owner from " + obj.objname() + ", new owner list: " + obj.owners().map_to_english('objname) + ".");
    } with handler {
        .tell(traceback()[1][2]);
    }
.

protected trace_method_cmd
    arg cmdstr, cmd, ref;
    var method, current, trace, syn, minfo, line, anc, len;
    
    (> .perms(sender(), 'this) <);
    syn = cmd + " <object.method()>";
    if (ref[1] != 'method)
        return .tell_error(syn, toliteral(cmd) + " requires a full method reference.");
    catch any {
        method = ref[4];
        current = ref[2].find_method(method);
        trace = [];
        while (current) {
            trace = [@trace, current];
            current = (| ref[2].find_next_method(method, current) |);
        }
    } with handler {
        if (error() != ~stop)
            .tell(traceback()[1][2]);
    }
    .tell("Method trace of " + ref[2].objname() + "." + tostr(ref[4]) + "():");
    len = .linelen();
    for anc in (trace.reverse()) {
        minfo = ._get_method_info(anc, method);
        line = minfo[4] ? "+" | " ";
        line = line + tostr(minfo[5]).right(4) + " ";
        line = line + anc.objname() + ".";
        line = line + tostr(minfo[2]) + "(" + minfo[3] + ")";
        line = line.pad(len / 2);
        line = line + minfo[6];
        line = line.chop(len);
        .tell(line);
    }
.

protected evaluate
    arg str;
    var time, ticks, mtime, result;
    
    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 = 1000000 - mtime[1] + mtime[2];
    else if (mtime[1] > mtime[2] && time)
        mtime = time * 1000000 + (1000000 - mtime[1]) + mtime[2];
    else
        mtime = mtime[2] - mtime[1];
    return [[ticks, time, mtime], result];
.

protected _list_method
    arg obj, method, [args];
    var out, code, line, opt, nums, opt, state, flags, f;
    
    (> .perms(sender(), 'this) <);
    nums = [@args, 0][1];
    opt = [@args, "", ""][2];
    code = obj.list_method(method);
    if (nums) {
        code = code.numbered_text();
    } else {
        for line in [1 .. code.length()]
            code = code.replace(line, "  " + code[line]);
    }
    switch (obj.method_state(method)) {
        case 'root:
            line = "+root";
        case 'driver:
            line = "+driver";
        case 'public:
            line = "+public";
        case 'protected:
            line = "+protected";
        case 'private:
            line = "+private";
    }
    opt = (opt ? opt + " " | "") + line;
    flags = obj.method_flags(method);
    if ('disallow_overrides in flags)
        opt = opt + " +disallow";
    if ('synchronized in flags)
        opt = opt + " +sync";
    if ('locked in flags)
        opt = opt + " +locked";
    line = obj.objname() + "." + tostr(method) + " " + opt;
    if (nums)
        out = ["--- Method " + line];
    else
        out = ["@program " + line];
    out = out + code + ["."];
    return out;
.

protected _display_variables
    arg obj, info, chop, comments;
    var line, tidbit, len;
    
    (> .perms(sender(), 'this) <);
    len = .linelen();
    for tidbit in (info) {
        line = "  " + (tidbit[1] != obj ? tidbit[1].objname() | "") + ",";
        line = line + tostr(tidbit[2]) + ": ";
        line = line + $data_lib.unparse(tidbit[3]);
        if (chop)
            line = line.chop(len);
        .tell(line);
    }
.

protected mmod_cmd
    arg cmdstr, cmd, str;
    var args, options, ref, obj, meth, opt, info, line, def, mname, flags, b;
    
    options = [["dr?iver"], ["r?oot"], ["pub?lic"], ["priv?ate"], ["prot?ected"], ["di?sallow_overrides"], ["s?yncronized"], ["l?ocked"], ["n?ative"]];
    if (!str) {
        .tell(["", "=> Syntax: " + cmd + " <method> <method flags>", "", "Where flags can be any of:", ""]);
        for opt in (options.slice(1))
            .tell("    +|-" + opt);
        return;
    }
    args = $parse_lib.getopt(str, options);
    catch any {
        ref = $parse_lib.xreference(args[1].to_string());
    } with handler {
        return traceback()[1][2];
    }
    if (!ref || ref[1] != 'method)
        return "Invalid object.method reference.";
    meth = ref[4];
    obj = ref[3];
    mname = ref[3].objname() + "." + tostr(ref[4]) + "()";
    if (!ref[2].is_writable_by(this()))
        return "You cannot write to " + ref[2].namef('xref) + ".";
    def = (| ref[3].find_method(ref[4]) |);
    if (!def)
        return "Method " + mname + " does not exist.";
    if (def != ref[3])
        return ref[2].objname() + " does not define " + mname;
    if (!args[2]) {
        return .display_cmd("", "", ref[3].objname() + "." + tostr(ref[4]) + "*");
        info = ._get_method_info(obj, meth);
        switch (obj.method_state(meth)) {
            case 'public:
                .tell("Public Method:");
            case 'private:
                .tell("Private Method:");
            case 'protected:
                .tell("Protected Method:");
        }
        return [$object_lib.parse_method_flags(info[4]) + tostr(info[5]).right(4) + " " + info[1].objname() + "." + tostr(info[2]) + "(" + info[3] + ")"];
    }
    flags = obj.method_flags(meth);
    if ('locked in flags)
        return mname + " is locked, and cannot be changed.";
    if ('native in flags)
        return mname + " is native, and cannot be changed.";
    for opt in (args[2]) {
        switch (opt[1]) {
            case "pub?lic":
                obj.set_method_state(meth, 'public);
                .tell("Method " + mname + " set to Public.");
            case "r?oot":
                obj.set_method_state(meth, 'root);
                .tell("Method " + mname + " set to Root.");
            case "dr?iver":
                obj.set_method_state(meth, 'driver);
                .tell("Method " + mname + " set to Driver.");
            case "priv?ate":
                obj.set_method_state(meth, 'private);
                .tell("Method " + mname + " set to Private.");
            case "prot?ected":
                obj.set_method_state(meth, 'protected);
                .tell("Method " + mname + " set to Protected.");
            case "di?sallow_overrides":
                flags = obj.method_flags(meth).setadd('disallow_overrides);
                obj.set_method_flags(meth, flags);
                .tell("Method " + mname + " flags are now: " + $object_lib.parse_method_flags(flags));
            case "s?yncronized":
                flags = obj.method_flags(meth).setadd('synchronized);
                obj.set_method_flags(meth, flags);
                .tell("Method " + mname + " flags are now: " + $object_lib.parse_method_flags(flags));
            case "l?ocked":
                .tell("You cannot set the locked flag.");
            case "n?ative":
                .tell("You cannot set the native flag.");
            default:
                .tell("Unable to process flag: \"" + (opt[3] ? "+" | "-") + opt[2] + "\".");
        }
    }
.

public parse_methodcmd_options
    arg syntax, args, [more];
    var o, opt, opts, out, r;
    
    o = [@more, []][1] + [["pub?lic"], ["r?oot"], ["dr?iver"], ["pri?vate"], ["pro?tected"], ["di?sallow_overrides"], ["s?yncronized"], ["l?ocked"], ["n?ative"]];
    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 = [@out, "  +|-" + opt[1]];
        (> .tell_error(syntax, ["Valid options:"] + out.lcolumnize()) <);
    }
    r = (| $parse_lib.xreference(args[1].to_string()) |);
    if (!r) {
        opts = opts.add('error, "Invalid <object>.<method> reference.");
        opts = opts.add('ignore, 1);
    }
    if (r[2] && !r[2].is_writable_by(this())) {
        opts = opts.add('error, "You cannot program " + r[2].objname() + ".");
        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_state(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].strip("?").to_symbol());
            case "di?sallow_overrides", "s?yncronized":
                opts = opts.add('mflags, opts['mflags].setadd(opt[1].strip("?").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].strip("?").to_symbol(), [opt[3], opt[4]]);
        }
    }
    return opts;
.

protected chmanage_cmd
    arg cmdstr, cmd, args;
    var obj, manager;
    
    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 handler {
        return traceback()[1][2];
    }
    return "Manager on " + obj.namef('xref) + " changed to " + manager.namef('xref) + ".";
.

protected managed_cmd
    arg cmdstr, cmd, args;
    var manager, managed, obj, out, len;
    
    manager = (| .match_environment(args) |);
    if (!manager) {
        manager = (| $user_db.find(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)
        out = out + ["  " + obj.namef('xref).pad(len) + " " + $object_lib.see_perms(obj, ["", ""])];
    return out;
.

protected trusted_by_cmd
    arg cmdstr, cmd, args;
    var target, trusted, obj, out, len;
    
    target = (| .match_environment(args) |);
    if (!target) {
        target = (| $user_db.find(args) |);
        if (!target)
            return "Unable to find \"" + args + "\".";
    }
    trusted = target.trusted_by();
    if (!trusted)
        return target.namef('ref) + " is not in any object's trusted list.";
    out = [target.namef('ref) + " is in the trusted list for:"];
    return out + $object_lib.generate_object_listing('trusted_by, 'trusted, 'literal);
.

public create_cmd
    arg cmdstr, cmd, args;
    var new, parents, obj;
    
    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 = parents + [.match_env_nice(obj)];
.

public make_patch_cmd
    arg cmdstr, com, args;
    var objs, obj, tmp, last, out, line, p, n;
    
    objs = out = [];
    for tmp in (args.explode()) {
        if (tmp[1] == "-") {
            out = out + ["old " + tmp.subrange(2)];
            continue;
        }
        objs = objs + [.match_env_nice(tmp)];
    }
    for obj in (objs) {
        n = obj.objname();
        p = obj.parents();
        line = "object " + n;
        if (obj.is($named))
            line = line + " \"" + obj.name() + "\"";
        out = out + ["", line];
        for tmp in (p.map('objname))
            out = out + ["parent " + tmp];
    }
    return out;
.


parent $player
object $storyteller

var $root objname 'storyteller
var $root created_on 809928922
var $root inited 1
var $root flags ['parameters, 'methods, 'code, 'core]
var $has_commands shortcuts #[]
var $has_commands remote #[]
var $has_commands local #[]
var $player strength [0, 0]
var $player agility [0, 0]
var $player appearance [0, 0]
var $player health [0, 0]
var $player life [0, 0]
var $player intellect [0, 0]
var $player knowledge [0, 0]
var $player backbone [0, 0]
var $player charisma [0, 0]
var $player humanity [0, 0]
var $player perception [0, 0]
var $player presence [0, 0]
var $player source [0, 0]
var $player weapons []



parent $located_location
object $on_location

var $root objname 'on_location
var $root created_on 809991549
var $root inited 1
var $root flags ['parameters, 'methods, 'code, 'core]
var $has_commands shortcuts #[]
var $has_commands remote #[]
var $has_commands local #[]
var $has_settings defined_settings #[]
var $has_settings local_settings #[]
var $has_settings settings #[]
var $named name ['uniq, "located_location_2", "the located_location_2"]
var $named name_aliases []
var $gendered gender $gender_neuter
var $described prose []
var $location contents []
var $located location $nowhere
var $located obvious 1

public description
    arg flags;
    var line;
    
    line = "You see " + .contents().map_to_english('namef) + " on " + .name() + ".";
    return (> pass(flags) <) + [line];
.



parent $located_location
object $in_location

var $root objname 'in_location
var $root created_on 809991552
var $root inited 1
var $root flags ['parameters, 'methods, 'code, 'core]
var $has_commands shortcuts #[]
var $has_commands remote #[]
var $has_commands local #[]
var $has_settings defined_settings #[]
var $has_settings local_settings #[]
var $has_settings settings #[]
var $named name ['uniq, "located_location_3", "the located_location_3"]
var $named name_aliases []
var $gendered gender $gender_neuter
var $described prose []
var $location contents []
var $located location $nowhere
var $located obvious 1

public description
    arg flags;
    var line;
    
    line = "You see " + .contents().map_to_english('namef) + " in " + .name() + ".";
    return (> pass(flags) <) + [line];
.


parent $log
object $http_log

var $root inited 1
var $root objname 'http_log
var $root created_on 796268969
var $root flags ['methods, 'code, 'core]
var $root writers [$http_lib, $false]
var $messaged verbs #[]
var $messaged messages #[]
var $public public ['readable]
var $text text []
var $named name ['prop, "HTTP Log", "HTTP Log"]
var $named name_aliases []
var $gendered gender $gender_neuter
var $described prose []
var $located location $nowhere
var $located obvious 1
var $http_log tally 3896

public log
    arg [args];
    
    tally = tally + 1;
.

public tally
    return tally;
.



parent $log
object $reaper_log

var $root inited 1
var $root objname 'reaper_log
var $root created_on 796268969
var $root flags ['methods, 'code, 'core]
var $public public ['readable]
var $text text []
var $named name ['prop, "Reaper Logfile", "Reaper Logfile"]
var $named name_aliases []
var $gendered gender $gender_neuter
var $messaged verbs #[]
var $messaged messages #[]
var $described prose []
var $located location $nowhere
var $located obvious 1



parent $log
object $login_log

var $root inited 1
var $root trusted [$login_interface]
var $root objname 'login_log
var $root created_on 796268969
var $root flags ['methods, 'code, 'core]
var $messaged verbs #[]
var $public public ['readable]
var $text text []
var $named name ['uniq, "log_3", "the log_3"]
var $named name_aliases []
var $gendered gender $gender_neuter
var $described prose []
var $located location $nowhere
var $located obvious 1



parent $mail_list
object $mail_list_news

var $root fertile 1
var $root inited 1
var $root objname 'mail_list_news
var $root created_on 796268969
var $root flags ['parameters, 'methods, 'code, 'fertile, 'core]
var $root indestructable 1
var $named name ['prop, "news", "news"]
var $named name_aliases []
var $mail_list mail []
var $mail_list senders 1
var $mail_list readers 1
var $mail_list notify []
var $mail_list last_received_on 812313975



parent $mail_list
object $mail_list_bugs

var $root objname 'mail_list_bugs
var $root created_on 811630223
var $root inited 1
var $root flags ['parameters, 'methods, 'code, 'core]
var $has_settings defined_settings #[]
var $has_settings local_settings #[]
var $has_settings settings #[]
var $named name ['prop, "bugs", "bugs"]
var $named name_aliases []
var $mail_list mail []
var $mail_list senders 1
var $mail_list readers 1
var $mail_list notify []
var $mail_list last_received_on 812141528



parent $registry
object $place_db

var $root trusted 1
var $root inited 1
var $root objname 'place_db
var $root created_on 796268969
var $root flags ['parameters, 'methods, 'code, 'core]
var $db database #[["void", $void], ["nowhere", $nowhere], ["The Pit", $the_pit]]
var $registry stripped_characters "!@#$%^&*()_+-=~`'{}[]|/?\"\\,.<>;:"

public place_destroyed
    // called in $place.uninit_place (incase the place is in the db)
.



parent $registry
object $user_db

var $root trusted [$user]
var $root inited 1
var $root objname 'user_db
var $root created_on 796268969
var $root flags ['methods, 'parameters, 'code, 'core]
var $root indestructable 1
var $db database #[["GenericUserObject", $user], ["GenericBuilder", $builder], ["GenericGuestObject", $guest], ["Reaper", $reaper], ["NoOne", $no_one], ["GenericProgrammer", $programmer], ["GenericAdmin", $admin], ["player", $player]]
var $user_db connected []
var $user_db invalid_chars "$#@!^&%~"
var $registry stripped_characters "!@#$%^&*()_+-=~`'{}[]|/?\"\\,.<>;: "
var $registry reserved_names ["user", "builder", "programmer", "admin", "housekeeper", "Reaper", "noone", "guest", "a", "i", "an", "your", "you'r", "me", "god"]
var $registry invalid_names ["ass", "cunt", "fuck", "shit", "damn", "the"]
var $registry min_char_len 3
var $registry max_char_len 20

public users
    return .database().keys();
.

public connected
    var x;
    
    for x in (connected) {
        if (!valid(x) || (| !x.connections() |))
            connected = connected.setremove(x);
    }
    return connected;
.

public did_connect
    (> .perms(caller(), $user) <);
    connected = connected.setadd(sender());
.

public did_disconnect
    .perms(caller(), $user);
    connected = connected.setremove(sender());
.

public valid_name
    arg name;
    
    if ($string.strip(name, invalid_chars).length() < name.length())
        throw(~invname, "Names cannot contain any of '" + invalid_chars + "'.");
    return (> pass(name) <);
.

public match
    arg name;
    
    return (> .find(name) <);
.

public total_connected
    return .connected().length();
.

public clean_user_db
    var key, db, invalid;
    
    connected = [];
    db = .database();
    invalid = [];
    for key in (.database().keys()) {
        if (!valid(db[key])) {
            .remove(key);
            invalid = [@invalid, key];
        }
    }
    return ["Invalid $user_db entries: " + invalid.to_english()];
.



parent $cml2_evaluator
object $cml2_format

var $root objname 'cml2_format
var $root child_index 4
var $root fertile 1
var $root created_on 806313297
var $root inited 1
var $root flags ['parameters, 'methods, 'code, 'fertile, 'core]

public eval_ctext
    arg data, vars;
    var ret, v, k;
    
    v = .init();
    for k in (v.keys())
        vars = vars.add(k, v[k]);
    ret = ._eval_ctext(vars, data);
    if (!ret[2]['newline])
        return ret[1] + "\\n";
    else
        return ret[1];
.

public _eval_ctext
    arg vars, data;
    var out, uflags, ret, token;
    
    out = "";
    if (type(data) != 'list)
        data = [data];
    for token in (data) {
        switch (type(token)) {
            case 'frob:
                switch (class(token)) {
                    case $ctext_generator:
                        ret = vars['evaluator].eval_generator(vars, token);
                        token = ret[1];
                        vars = ret[2];
                        catch ~type {
                            out = out + token;
                        } with handler {
                            ret = ._eval_ctext(vars, token);
                            out = out + token;
                        }
                    case $ctext_format:
                        ret = .eval_formatter(vars, token);
                        token = ret[1];
                        vars = ret[2];
                        out = out + token;
                    default:
                        out = out + token;
                }
            case 'string:
                out = out + token;
            case 'list:
                ret = ._eval_ctext(vars, token);
                token = ret[1];
                out = out + token;
                vars = ._check_newline(ret[2], token);
            default:
                out = out + token;
        }
        if (token.length() >= 3 && token.subrange(token.length() - 1) == "\\n")
            vars = vars.add('newline, 1);
        else
            vars = vars.add('newline, 0);
    }
    return [out, vars];
.

public do__ln
    arg vars, flags, args;
    
    return ["", vars.add('line, vars['line] + 1)];
.

public eval_generator
    arg vars, gen;
    
    return vars['evaluator].eval_generator(vars, gen);
.

public gen_group
    arg vars, flags, args;
    
    return ._eval_ctext(vars, args);
.

public do_group
    arg vars, flags, args;
    var out, token, ret;
    
    ret = ._eval_ctext(vars, args);
    return ret;
.

public _check_newline
    arg vars, str;
    
    if (str && str.last() == "\\n")
        return vars.add('newline, 1);
    else
        return vars.add('newline, 0);
.

public eval_formatter
    arg vars, form;
    var flags, key, value, ret;
    
    flags = form.ctext_uflags();
    for key in (flags.keys()) {
        ret = ._eval_ctext(flags[key], vars);
        form = form.add_ctext_flag(key, ret[1]);
        vars = ret[2];
    }
    catch ~methodnf {
        ret = .(form.method())(vars, form.ctext_flags(), form.args());
    } with handler {
        return [">>ERROR: unknown formatter " + form.name() + ".<<", vars];
    }
    return [ret[1], ret[2]];
.



parent $cml2_evaluator
object $cml2_base_eval

var $root objname 'cml2_base_eval
var $root child_index 1
var $root created_on 806303080
var $root inited 1
var $root flags ['parameters, 'methods, 'code, 'core]

public gen_server_name
    arg vars, flags, args;
    
    return [$motd.server_name(), vars];
.

public gen_set
    arg vars, flags, args;
    var ret, name;
    
    ret = ._eval_ctext(vars, [args[1]]);
    name = ret[1][1];
    ret = ._eval_ctext(vars, [args[2]]);
    return [[""], vars.add(name, ret[1])];
.

public gen_name
    arg vars, flags, args;
    var ret, out, name;
    
    ret = ._eval_ctext(vars, args);
    args = ret[1];
    vars = ret[2];
    out = [];
    if (!args)
        out = [vars['this].name()];
    for name in (args) {
        switch (type(name)) {
            case 'dbref:
                name = name.name();
            case 'string:
                catch ~objnf {
                    name = vars['this].match_environment(name).name();
                } with handler {
                    name = name;
                }
        }
        out = [@out, name];
    }
    return [out, vars];
.

public gen_english
    arg vars, flags, args;
    var out, item, ret, sep, empty;
    
    sep = ((| flags["sep"] |) || ",") + " ";
    empty = (| flags["empty"] |) || "nothing";
    ret = ._eval_ctext(vars, args);
    args = ret[1];
    vars = ret[2];
    out = [];
    if (args.length() == 0)
        return [[empty], vars];
    if (args.length() == 1)
        return [args, vars];
    if (args.length() == 2)
        return [[args[1], " and ", args[2]], vars];
    out = [@out, args[1]];
    for item in (args.chop().subrange(2))
        out = [@out, sep, item];
    out = [@out, " and ", args.last()];
    return [out, vars];
.

public gen_group
    arg vars, flags, args;
    
    return ._eval_ctext(vars, args);
.

public gen_def
    arg vars, flags, args;
    var ret, name;
    
    ret = ._eval_ctext(vars, [args[1]]);
    name = ret[1][1];
    return [[""], vars.add(name, args[2])];
.

public gen_foreach
    arg vars, flags, args;
    var v, list, body, out, item, ret;
    
    v = (| flags["var"] |) || "##";
    list = flags["list"];
    out = [];
    for item in (list) {
        vars = vars.add(v, item);
        ret = ._eval_ctext(vars, args);
        if (vars['time] == 'format)
            out = out.join(ret[1]);
        else
            out = [@out, @ret[1]];
        vars = ret[2];
    }
    return [out, vars];
.

public gen_time
    arg vars, flags, args;
    var out, ret, a, word;
    
    ret = ._eval_ctext(vars, args);
    args = ret[1];
    vars = ret[2];
    if (!args)
        return [tostr(time()), vars];
    out = "";
    for a in (args) {
        if (type(a) == 'string && "%" in a)
            out = out + (out ? " " | "") + $time.format(a);
        else
            out = out + (out ? " " | "") + a;
    }
    return [out, vars];
.

public gen_vars
    arg vars, flags, args;
    var out, v;
    
    out = [];
    for v in (vars.keys()) {
        if (type(v) == 'string)
            out = [@out, v];
    }
    return [out, vars];
.

public gen_get
    arg vars, flags, args;
    var name, ret;
    
    ret = ._eval_ctext(vars, args);
    args = ret[1];
    vars = ret[2];
    name = args[1];
    return [[vars[name]], vars];
.



parent $cml2_evaluator
object $cml2_uncompiler

var $root objname 'cml2_uncompiler
var $root created_on 806303067
var $root inited 1
var $root flags ['parameters, 'methods, 'code, 'core]

public eval_ctext
    arg data, [vars];
    var ret;
    
    vars = [@vars, #[]][1];
    vars = vars.add('line, 1);
    ret = ._eval_ctext(vars, data);
    return ret[1];
.

public eval_generator
    arg vars, gen;
    var out, flags, key, value, ret;
    
    catch ~methodnf {
        ret = .(tosym("gen_" + gen.name()))(vars, gen.ctext_flags(), gen.args());
        return [ret[1], ret[2]];
    } with handler {
        out = ["[" + gen.name()];
        flags = gen.ctext_flags();
        for key in (flags.keys()) {
            if (flags[key] == 1) {
                out = out.join(" " + key);
            } else if (type(flags[key]) == 'list) {
                ret = (> ._eval_ctext(vars, [flags[key]]) <);
                out = out.join(" " + key + "=" + ret[1][1]);
                vars = ret[2];
            } else {
                ret = (> ._eval_ctext(vars, flags[key]) <);
                out = out.join(" " + key + "=" + ret[1][1]);
                vars = ret[2];
            }
        }
        if (gen.args())
            out = out.join(":");
        for key in (gen.args()) {
            ret = (> ._eval_ctext(vars, [key]) <);
            out = out.join([" "].join(ret[1]));
            vars = ret[2];
        }
        return [out.join("]"), vars];
    }
.

public _eval_ctext
    arg vars, data;
    var out, uflags, ret, token, a;
    
    out = [];
    if (type(data) != 'list)
        data = [data];
    while (data) {
        token = data[1];
        data = data.subrange(2);
        switch (type(token)) {
            case 'frob:
                if (class(token) == $ctext_generator) {
                    ret = (> .eval_generator(vars, token) <);
                    out = out.join(ret[1]);
                    vars = ret[2];
                } else if (class(token) == $ctext_format) {
                    ret = (> .eval_formatter(vars, token) <);
                    out = out.join(ret[1]);
                    vars = ret[2];
                } else {
                    out = out.join(token);
                }
            case 'string:
                out = out.join(token);
            case 'list:
                out = out.join("[:");
                for a in (token) {
                    ret = ._eval_ctext(vars, a);
                    out = out.join(" ").join(ret[1]);
                    vars = ret[2];
                }
                out = out.join("]");
            default:
                out = out.join($data_lib.unparse(token));
        }
    }
    return [out, vars];
.

public do__ln
    arg vars, flags, args;
    
    return [[""], vars];
.

public do_dl
    arg vars, flags, args;
    var out, ret, key, a;
    
    out = "dl";
    for key in (flags.keys()) {
        if (flags[key] == 1) {
            out = out + " " + key;
        } else {
            ret = ._eval_ctext(vars, flags[key]);
            out = out + " " + key + "=" + ret[1][1];
            vars = ret[2];
        }
    }
    out = ["", "{" + out + "}", ""];
    for a in (args) {
        ret = ._eval_ctext(vars, a);
        out = out.join(ret[1]);
        vars = ret[2];
    }
    out = out.join(["{-dl}", ""]);
    return [out, vars];
.

public do_dd
    arg vars, flags, args;
    var ret, out;
    
    out = ["", "{dd}"];
    ret = ._eval_ctext(vars, args);
    out = out.join(ret[1]).join("{-dd}");
    return [[@out, ""], ret[2]];
.

public gen_group
    arg vars, gen;
    var out, a, ret;
    
    out = ["[:"];
    for a in (gen.args()) {
        ret = ._eval_ctext(vars, a);
        out = out.join(" ").join(ret[1]);
        vars = ret[2];
    }
    return [out.join("]"), vars];
.

public do_group
    arg vars, flags, args;
    var ret, out;
    
    out = ["", "{:"];
    ret = (> ._eval_ctext(vars, args) <);
    out = out.join(ret[1]).join("}");
    return [out, ret[2]];
.

public eval_formatter
    arg vars, gen;
    var out, flags, key, value, ret;
    
    catch ~methodnf {
        ret = .(tosym("do_" + gen.name()))(vars, gen.ctext_flags(), gen.args());
        return [ret[1], ret[2]];
    } with handler {
        out = ["{" + gen.name()];
        flags = gen.ctext_flags();
        for key in (flags.keys()) {
            if (flags[key] == 1) {
                out = out.join(" " + key);
            } else {
                ret = ._eval_ctext(vars, flags[key]);
                out = out.join(" " + key + "=" + ret[1][1]);
                vars = ret[2];
            }
        }
        flags = gen.ctext_uflags();
        for key in (flags.keys()) {
            if (flags[key] == 1) {
                out = out.join(" " + key);
            } else {
                ret = ._eval_ctext(flags[key], vars);
                out = out.join(" " + key + "=" + ret[1][1]);
                vars = ret[2];
            }
        }
        if ($cml2_form.is_block(gen.name())) {
            out = out.join("}");
            for key in (gen.args()) {
                ret = ._eval_ctext(vars, [key]);
                out = out.join(ret[1]);
                vars = ret[2];
            }
            out = out.join("{-" + gen.name() + "}");
        } else if ($cml2_form.is_field(gen.name())) {
            out = out.join("}");
            for key in (gen.args()) {
                ret = ._eval_ctext(vars, [key]);
                out = out.join(ret[1]);
                vars = ret[2];
            }
        } else {
            out = out.join("}");
        }
        return [out, vars];
    }
.



parent $lock_frob
object $or_lock_frob

var $root objname 'or_lock_frob
var $root created_on 796268969
var $root flags ['parameters, 'methods, 'code, 'core]

public new
    arg lhs, rhs;
    
    if (type(lhs) != 'frob || type(rhs) != 'frob)
        throw(~perm, "Arguments are not both frobs.");
    return (<this(), [lhs, rhs]>);
.

public try
    arg lock, obj;
    
    return lock[1].try(obj) || lock[2].try(obj);
.

public lock_name
    arg lock, [type];
    
    type = [@type, 'literal][1];
    switch (type) {
        case 'literal:
            return "(" + lock[1].lock_name(type) + " || " + lock[2].lock_name(type) + ")";
        default:
            return lock[1].lock_name(type) + " or " + lock[2].lock_name(type);
    }
.



parent $lock_frob
object $true_lock_frob

var $root objname 'true_lock_frob
var $root created_on 796268969
var $root flags ['parameters, 'methods, 'code, 'core]
var $lock_frob names #[['default, "true"], ['exit, "anybody"], ['str, "any"]]

public new
    return (<this(), []>);
.

public try
    arg lock, obj;
    
    return 1;
.



parent $lock_frob
object $false_lock_frob

var $root objname 'false_lock_frob
var $root created_on 796268969
var $root flags ['parameters, 'methods, 'code, 'core]
var $lock_frob names #[['str, "none"], ['exit, "nobody"], ['default, "false"]]

public new
    return (<this(), []>);
.

public try
    arg lock, obj;
    
    return 0;
.



parent $lock_frob
object $not_lock_frob

var $root objname 'not_lock_frob
var $root created_on 796268969
var $root flags ['parameters, 'methods, 'code, 'core]

public new
    arg lock;
    
    if (type(lock) != 'frob)
        throw(~perm, "Argument is not a lock.");
    return (<this(), [lock]>);
.

public try
    arg lock, obj;
    
    return !lock[1].try(obj);
.

public lock_name
    arg lock, [type];
    
    type = [@type, 'literal][1];
    switch (type) {
        case 'literal:
            return "(!" + lock[1].lock_name(type) + ")";
        default:
            return "not " + lock[1].lock_name(type);
    }
.



parent $lock_frob
object $object_lock_frob

var $root objname 'object_lock_frob
var $root created_on 796268969
var $root flags ['parameters, 'methods, 'code, 'core]

public new
    arg obj;
    
    if (type(obj) != 'dbref)
        throw(~perm, "Argument is not a dbref.");
    return (<this(), [obj]>);
.

public try
    arg lock, obj;
    
    return lock[1] == obj || obj == sender() || $sys.is_system(obj);
.

public test
    arg testlist, testee, [args];
    var x;
    
    for x in (testlist) {
        if (testee == x)
            return 1;
    }
    return 0;
.

public lock_name
    arg value, [type];
    
    return value[1].name();
.



parent $lock_frob
object $and_lock_frob

var $root objname 'and_lock_frob
var $root created_on 796268969
var $root flags ['parameters, 'methods, 'code, 'core]

public new
    arg lhs, rhs;
    
    if (type(lhs) != 'frob || type(rhs) != 'frob)
        throw(~perm, "Arguments are not both frobs.");
    return (<this(), [lhs, rhs]>);
.

public try
    arg lock, obj;
    
    return lock[1].try(obj) && lock[2].try(obj);
.

public lock_name
    arg lock, [type];
    
    type = [@type, 'literal][1];
    switch (type) {
        case 'literal:
            return "(" + lock[1].lock_name(type) + " && " + lock[2].lock_name(type) + ")";
        default:
            return lock[1].lock_name(type) + " and " + lock[2].lock_name(type);
    }
.



parent $lock_frob
object $inside_lock_frob

var $root trusted []
var $root inited 1
var $root objname 'inside_lock_frob
var $root created_on 796268969
var $root flags ['parameters, 'methods, 'code, 'core]

public try
    arg objs, obj;
    var o;
    
    for o in (objs) {
        if (o.contains(obj))
            return 1;
    }
    return 0;
.



parent $programmer
object $admin

var $root owners [$admin]
var $root inited 1
var $root owned [$admin]
var $root manager $admin
var $root quota 75000
var $root objname 'admin
var $root created_on 796268969
var $root flags ['parameters, 'methods, 'code, 'core]
var $root managed [$admin]
var $messaged verbs #[]
var $location contents []
var $located location $body_cave
var $located obvious 1
var $user password "*"
var $user connected_at 0
var $user last_command_at 0
var $user connections []
var $user modes #[]
var $user prompt ""
var $user parsers [$command_parser]
var $user tell_traceback ['verbose, 4]
var $user context #[]
var $user filters []
var $programmer eval_prefix #[["me", "me = this()"], ["here", "here = this().location()"]]
var $programmer eval_offset #[]
var $admin shutdown_started 0
var $command_aliases command_aliases []
var $mail_list letters #[]
var $mail_list letters_index #[]
var $mail_list senders 1
var $mail_list readers []
var $mail_list notify [$admin]
var $mail_list last_letter 0
var $mail_list mail []
var $mail_ui subscribed #[[$admin, 791485891]]
var $mail_ui current #[['location, 0], ['list, $admin]]
var $gendered gender $gender_neuter
var $described prose []
var $named name ['prop, "Generic Admin", "Generic Admin"]
var $named name_aliases []
var $user_data user_data #[['real_name, [1, "???"]], ['email, [1, "???"]]]
var $has_commands local #[["@task?s", [["@task?s", "", "@task?s", 'tasks_cmd, #[]]]], ["@del-t?ask|@kill", [["@del-t?ask|@kill", "*", "@del-t?ask|@kill <any>", 'del_task_cmd, #[[1, ['any, []]]]]]], ["@backup", [["@backup", "", "@backup", 'backup_cmd, #[]]]], ["@shutdown", [["@shutdown", "*", "@shutdown <any>", 'shutdown_cmd, #[[1, ['any, []]]]]]], ["@adjust", [["@adjust", "user|att?itude * to *", "@adjust user|att?itude <any> to <any>", 'adjust_user_cmd, #[[2, ['any, []]], [4, ['any, []]]]]]], ["@ustat", [["@ustat", "*", "@ustat <any>", 'user_stat_cmd, #[[1, ['any, []]]]]]], ["@grep", [["@grep", "*", "@grep <any>", 'grep_cmd, #[[1, ['any, []]]]]]], ["@dump-grep|@dgrep", [["@dump-grep|@dgrep", "*", "@dump-grep|@dgrep <any>", 'dump_grep_cmd, #[[1, ['any, []]]]]]], ["@mojo", [["@mojo", "*", "@mojo <any>", 'mojo_cmd, #[[1, ['any, []]]]]]]]
var $help_ui indices [$help_index_root]
var $help_ui history [$help_summary]
var $help_ui current 1
var $command_cache shortcut_cache 0
var $command_cache remote_cache 0
var $command_cache local_cache 0

root init_admin
    $sys.new_admin();
.

public del_task_cmd
    arg cmdstr, com, task;
    var syn;
    
    (> .perms(sender(), 'this) <);
    (> .check_mojo() <);
    syn = com + " <task ID>";
    task = toint(task);
    if (!task)
        (> .tell_error(syn, "Tasks must be given as an integer") <);
    catch any {
        $scheduler.del_task(task);
    } with handler {
        (> .tell_error(syn, traceback()[1][2]) <);
    }
    .tell("Task number " + tostr(task) + " deleted.");
.

public tasks_cmd
    arg cmdstr, com;
    var out, task, task_queue, line, len_c1, time, args, fmt, tfmt;
    
    (> .perms(sender(), 'this) <);
    (> .check_mojo() <);
    task_queue = $scheduler.task_queue();
    if (task_queue) {
        out = [];
        tfmt = "%d %h %y %H:%M:%S";
        fmt = "%5L %20L %s";
        time = $time.format(tfmt);
        out = fmt.format("ID", "Exec Time", "Task");
        out = [out, fmt.format("---", "---------", "----")];
        fmt = fmt + ".%s(%s)";
        for task in (task_queue) {
            args = $data_lib.unparse(task[8]);
            out = out + [fmt.format(task[1], $time.format(tfmt, task[2] + task[3]), $data_lib.unparse(task[4]), task[6], args.subrange(2, args.length() - 2))];
        }
    
        // once tasks() returns more intelligent info, we can parse it here
        return out + ["---"];
    } else {
        .tell("*** NO TASKS ****");
    }
.

public backup_cmd
    arg cmdstr, com;
    
    (> .perms(sender(), 'this) <);
    (> .check_mojo() <);
    .tell("Starting backup.");
    $sys.do_backup(this());
    .tell("Done.");
.

public shutdown_cmd
    arg cmdstr, com, [args];
    var time, opt, why, answer;
    
    (> .perms(sender(), 'this) <);
    (> .check_mojo() <);
    args = $parse_lib.getopt(args, [["t?ime", 1]]);
    if (opt = "t?ime" in args[2].slice(1) && args[2][opt][4].is_numeric())
        time = toint(args[2][opt][4]);
    else
        time = 5;
    if (!why = args[1].to_string())
        why = "Administrator's whim.";
    .tell("Server shutdown in " + tostr(time) + " minutes");
    .tell("Reason for shutdown: " + why);
    ans = .prompt("Is this correct? [yes]: ");
    if (!ans)
        ans = "yes";
    if (!(ans in ["yes", "y"])) {
        .tell("I didn't think so, aborting shutdown...");
        return;
    }
    .tell("Ok!");
    $sys.do_shutdown(time, why);
.

public adjust_user_cmd
    arg cmdstr, com, com, who, com, what;
    var promos, wp, name, pwd;
    
    (> .perms(sender(), 'this) <);
    (> .check_mojo() <);
    what = what.explode();
    what = what[what.length()];
    promos = #[["user", ['user, $user]], ["builder", ['builder, $builder]], ["programmer", ['programmer, $programmer]], ["admin", ['admin, $admin]]];
    who = .match_env_nice(who);
    wp = who.parents();
    if (!(what in promos.keys()))
        $parse_lib.tell_error("Promote to any of: " + promos.keys().to_english() + ".");
    (| who.chparents(promos[what][2]) |);
    who.tell(.namef() + " has made you " + $string.a_or_an(what) + " " + what + ".");
    if ($guest in wp) {
        pwd = $code_lib.random_word();
        who.set_password(pwd);
        (| who.del_filter($wrap_filter) |);
        who.tell("Your password is currently \"" + pwd + "\".");
        who.tell("You can change it with the command: `@password`");
        who.set_title("");
    }
    who.rehash_caches();
    .tell("Ok, " + who.namef('titled) + " is now a " + what + ".");
.

public grep_cmd
    arg cmdstr, cmd, args;
    var what, from, obj, syn, opts, d, f;
    
    (> .perms(sender(), 'this) <);
    (> .check_mojo() <);
    syn = cmd + " <regexp> <object> [+|-d?escend] [+|-f?ull]";
    syn = [syn, "Flags default to -descend -full."];
    if (!args)
        .tell_error(@syn);
    args = $parse_lib.getopt(args, [["d?escend"], ["f?ull"]]);
    d = (| "d?escend" in args[2].slice(1) |);
    if (d)
        d = args[2][d][3];
    f = (| "f?ull" in args[2].slice(1) |);
    if (f)
        f = args[2][f][3];
    args = args[1];
    if (args.length() != 2)
        return .tell(syn);
    what = args[1];
    obj = .match_env_nice(args[2]);
    if (!what)
        return .tell(syn);
    .tell("Searching for \"" + what + "\"...");
    .grep(what, d, f, obj);
    .tell("---");
.

public egrep_cmd
    arg what;
    var found, x;
    
    (> .perms(sender(), 'this) <);
    (> .check_mojo() <);
    .tell("Searching...");
    found = .grep(what, 'regexp);
    for x in (found)
        .tell(x[1].name_str() + "." + tostr(x[2]) + " : " + tostr(x[3][1]));
    .tell("--- + Finis + ---");
.

public grep
    arg regexp, from, full, object;
    var objects, x, method, total;
    
    objects = [object];
    if (from)
        objects = [@objects, @object.descendants()];
    method = full ? '_grep_object_full | '_grep_object_brief;
    for object in (objects) {
        .(method)(regexp, object, full);
        total = total + 1;
        pause();
    }
    
    // fix later
    .tell(tostr(total) + " Object" + (total == 1 ? "" | "s") + " Searched.");
.

public user_stat_cmd
    arg cmdstr, cmd, str;
    var u, x, y, cs, line;
    
    (> .perms(sender(), 'this) <);
    (> .check_mojo() <);
    u = .find_object_nice(str, 'user, 'environment, 'grasp);
    line = "Random stats on user " + u.name();
    line = line + " <" + u.user_data('email) + ">";
    .tell(line + " [" + tostr($code_lib.user_type(u)) + "]:");
    x = u.quota_byte_usage();
    y = u.quota();
    line = " Quota: " + y.to_english() + "  Usage: " + x.to_english() + " Remaining: " + (y - x).to_english();
    .tell(line);
    if (u.connected()) {
        .tell(" Connections:");
        cs = u.connections();
        for x in [1 .. cs.length()] {
            line = "   " + cs[x].objname() + ": [";
            line = line + $time.ldate(cs[x].active_since(), 'date);
            line = line + "/" + $time.ltime(cs[x].active_since(), '24hr_sec);
            .tell(line + "] " + cs[x].address());
        }
    } else {
        .tell(" Last connected at " + $time.ltime(abs(u.connected_at())) + " " + $time.ldate(abs(u.connected_at())));
    }
    .tell(u.display_data());
.

public _grep_object
    arg regexp, obj, [how];
    var method, found, findings, what;
    
    (> .perms(sender(), 'this) <);
    findings = [];
    if (valid(obj)) {
        for method in (obj.methods()) {
            found = ._grep_text(regexp, obj.list_method(method), @how);
            if (found)
                findings = findings + [[obj, method, found]];
            pause();
        }
    }
    return findings;
.

private _grep_text
    arg regexp, text;
    var line, result, lines;
    
    lines = [];
    for line in [1 .. text.length()] {
        if ($string.match_regexp(regexp, text[line]))
            lines = lines + [[line, text[line]]];
    }
    return lines;
.

public dump_grep_cmd
    arg cmdstr, cmd, args;
    var what, from, obj, syn, opts, objs, found, local, anc;
    
    (> .perms(sender(), 'this) <);
    (> .check_mojo() <);
    syn = cmd + " <regexp> <object> [+|-d (descend)]";
    syn = [syn, "The descend flag defaults to +d."];
    if (!args)
        return .tell_error(@syn);
    opts = #[["d", [1, 0]]];
    args = $parse_lib.options(args, opts);
    opts = args[2];
    args = args[1];
    if (args.length() != 2)
        return .tell(syn);
    what = args[1];
    obj = .match_env_nice(args[2]);
    if (!what)
        return .tell(syn);
    if (opts["d"][1])
        objs = [obj] + obj.descendants();
    else
        objs = [objs];
    what = "*" + what + "*";
    found = [];
    for obj in (objs) {
        local = ._grep_object(what, obj);
        if (local)
            found = found + local;
        pause();
    }
    if (found) {
        .tell("-----starting output-----");
        for local in (found) {
            anc = local[1].find_method(local[2]);
            .tell("@program -e " + anc.objname() + "." + tostr(local[2]));
            .tell(anc.list_method(local[2]));
            .tell(".");
            pause();
        }
        .tell("-----finished output-----");
    } else {
        .tell("no objects found.");
    }
.

public dump_methgrep_cmd
    arg cmdstr, cmd, args;
    var what, from, obj, syn, opts, objs, found, local, anc;
    
    (> .perms(sender(), 'this) <);
    (> .check_mojo() <);
    syn = cmd + " <regexp> <object> [+|-d (descend)";
    syn = [syn, "The descend flag defaults to +d."];
    if (!args)
        return .tell_error(syn);
    opts = #[["d", [1, 0]]];
    args = $parse_lib.options(args, opts);
    opts = args[2];
    args = args[1];
    if (args.length() != 2)
        return .tell(syn);
    what = args[1];
    obj = .match_env_nice(args[2]);
    if (!what)
        return .tell(syn);
    if (opts["d"][1])
        objs = [obj] + obj.descendants();
    else
        objs = [objs];
    what = "*" + what + "*";
    found = [];
    for obj in (objs) {
        local = ._methgrep_object(what, obj);
        if (local)
            found = found + local;
        pause();
    }
    if (found) {
        .tell("-----starting output-----");
        for local in (found) {
            anc = local[1].find_method(local[2]);
            .tell("@program -e " + anc.objname() + "." + tostr(local[2]));
            .tell(anc.list_method(local[2]));
            .tell(".");
            pause();
        }
        .tell("-----finished output-----");
    } else {
        .tell("no objects found.");
    }
.

public _methgrep_object
    arg regexp, obj, [how];
    var method, found, findings, what;
    
    (> .perms(sender(), 'this) <);
    findings = [];
    if (valid(obj)) {
        for method in (obj.methods()) {
            if ($string.match_pattern(regexp, tostr(method)))
                findings = findings + [[obj, method, found]];
        }
    }
    return findings;
.

public mojo
    arg [args];
    var name;
    
    if (args)
        name = "Your ";
    else
        name = .name() + "'s ";
    (> .perms(sender(), 'this) <);
    if (this() in $sys.system())
        return name + "eyes glow from Mojo.";
    else
        return name + "eyes do not glow from Mojo.";
.

public mojo_cmd
    arg cmdstr, cmd, str;
    var syn, line;
    
    (> .perms(sender(), 'this) <);
    syn = cmd + " on|off";
    if (!str || !(str in ["on", "up", "off", "down"]))
        .tell_error(syn, .mojo(1));
    switch (str) {
        case "on", "up":
            .location().announce(.name() + "'s eyes begin to glow as Mojo courses through " + .gender().pronoun('po) + ".", this());
            $sys.add_to_system(this());
        case "off", "down":
            .location().announce(.name() + "'s eyes stop glowing as the Mojo leaves " + .gender().pronoun('pq) + " body.", this());
            $sys.del_from_system(this());
    }
    .tell(.mojo(1));
.

public description
    arg flags;
    
    return (> pass(flags) <) + [.mojo()];
.

public logout
    arg [args];
    
    (| $sys.del_from_system(this()) |);
    return (> pass(@args) <);
.

protected check_mojo
    if (!(this() in $sys.system())) {
        .tell("This command requires Mojo.");
        throw(~stop, "", 'no_traceback);
    }
.

public _grep_object_brief
    arg regexp, obj, [how];
    var method, f, findings, found, what, line;
    
    findings = [];
    if (valid(obj)) {
        for method in (obj.methods()) {
            found = ._grep_text(regexp, obj.list_method(method));
            if (found)
                findings = findings + [[method, found.slice(1)]];
            pause();
        }
    }
    line = obj.objname() + ".";
    for f in (findings)
        .tell(line + tostr(f[1]) + " line" + (f[2].length() == 1 ? " " | "s ") + f[2].to_english());
.

public _grep_object_full
    arg regexp, obj, [how];
    var method, found, findings, what, line;
    
    findings = [];
    line = obj.objname() + ".";
    if (valid(obj)) {
        for method in (obj.methods()) {
            for found in (._grep_text(regexp, obj.list_method(method)))
                .tell(line + tostr(method) + "() " + tostr(found[1]) + ": " + found[2]);
            pause();
        }
    }
    return findings;
.


parent $generic_editor
object $code_editor

var $root inited 1
var $root objname 'code_editor
var $root created_on 796268969
var $root flags ['parameters, 'methods, 'code, 'core]
var $command_aliases command_aliases []
var $has_commands local #[["compile", [["compile", "", "compile", 'compile_cmd, #[]]]], ["refresh", [["refresh", "", "refresh", 'refresh_cmd, #[]]]]]
var $command_cache shortcut_cache 0
var $command_cache remote_cache 0
var $command_cache local_cache 0

public start_editing
    arg what;
    var ref, obj, method, code;
    
    // called by $user_input.edit_cmd
    ref = $parse_lib.reference(what);
    obj = (| sender().match_environment(ref[1]) |);
    if (!obj) {
        sender().tell(what + " does not describe an editable method.");
        return 0;
    }
    method = tosym(ref[2]);
    catch any {
        code = sender().run_with_users_perms(obj, 'list_method, method, 2);
    } with handler {
        if (error() == ~methodnf) {
            code = [];
        } else {
            sender().tell("You cannot edit " + what + ".");
            sender().tell("Error: " + traceback()[1][2]);
            return 0;
        }
    }
    sender().tell("Now editing " + what + ".");
    code = $generic_editor.adjust(code, 'spaces);
    
    // user doing editing, obj to be programmed, method to be programmed, code, current line
    // number, changes made
    return #[['user, sender()], ['obj, obj], ['method, method], ['text, code], ['cur_line, 1], ['changed, 0]];
.

public compile_cmd
    arg editing, cmd;
    var errors, last_edit;
    
    (> .perms(sender(), 'this) <);
    catch any {
        last_edit = " // Edited: " + $time.ldate('mdy, 'dash) + "/";
        last_edit = last_edit + $time.ltime('12hr, '_ampm) + " ";
        last_edit = last_edit + "by: " + editing['user].namef('ref);
        errors = editing['user].run_with_users_perms(editing['obj], 'compile, editing['text] + [last_edit], editing['method]);
        if (errors) {
            editing['user].tell(errors);
        } else {
            editing['user].tell("Method compiled.");
    
            // clear modified flag
            editing = editing.replace('changed, 0);
        }
    } with handler {
        editing['user].tell("An error occurred attempting to compile.");
        editing['user].tell("The error was " + traceback()[1][2]);
    }
    return editing;
.

public refresh_cmd
    arg editing, com;
    var code;
    
    // re-loads method if not changed
    (> .perms(sender(), 'this) <);
    if (editing['changed]) {
        editing['user].tell("Changes have been made. Compile first to refresh.");
    } else {
        catch any {
            code = editing['user].run_with_users_perms(editing['obj], 'list_method, editing['method], 2);
        } with handler {
            editing['user].tell("Unable to list method into buffer, error was " + traceback()[1][2]);
            return editing;
        }
        editing = editing.replace('text, code);
        editing['user].tell("Done.");
    }
    return editing;
.

public help_msg
    var msg;
    
    msg = [];
    msg = msg + ["The code editor allows you to modify and re-compile methods."];
    msg = msg + ["It includes these commands it inherits from the generic editor:"];
    msg = msg + pass();
    msg = msg + [""];
    msg = msg + ["And adds the following:"];
    msg = msg + ["  compile      compile and store current text"];
    msg = msg + ["  refresh      re-load compiled method"];
    msg = msg + [""];
    return msg;
.



parent $generic_editor
object $note_editor

var $root inited 1
var $root objname 'note_editor
var $root created_on 796268969
var $root flags ['parameters, 'methods, 'code, 'core]
var $command_aliases command_aliases []
var $has_commands local #[["save", [["save", "", "save", 'save_cmd, #[]]]]]
var $command_cache shortcut_cache 0
var $command_cache remote_cache 0
var $command_cache local_cache 0

public start_editing
    arg what;
    var obj, text;
    
    // called by $user_input.edit_cmd
    obj = (| sender().match_environment(what) |);
    if (!obj || !(| obj.writeable_by(sender()) |)) {
        sender().tell("I don't see a note around here called " + what + " that you can edit.");
        return 0;
    }
    catch any {
        text = sender().run_with_users_perms(obj, 'text);
    } with handler {
        sender().tell("You cannot edit " + what + ".");
        sender().tell("Error: " + traceback()[1][2]);
        return 0;
    }
    sender().tell("Now editing " + what + ".");
    
    // user doing editing, obj edited, text, current line number, changes made
    return #[['user, sender()], ['obj, obj], ['text, text], ['cur_line, 1], ['changed, 0]];
.

public help_msg
    var msg;
    
    msg = [];
    msg = msg + ["The note editor allows you to write on notes interactively."];
    msg = msg + ["It includes these commands it inherits from the generic editor:"];
    msg = msg + pass();
    msg = msg + [""];
    msg = msg + ["And adds the following:"];
    msg = msg + ["  save         store text to note"];
    msg = msg + [""];
    return msg;
.

public save_cmd
    arg editing, cmd;
    var error;
    
    (> .perms(sender(), 'this) <);
    catch any {
        editing['user].run_with_users_perms(editing['obj], 'erase_cmd, "", "all", "", "");
        editing['user].run_with_users_perms(editing['obj], 'add_text, editing['text], $user);
        editing['user].tell("Done.");
    
        // clear modified flag
        editing = editing.replace('changed, 0);
    } with handler {
        editing['user].tell("An error occurred attempting to save.");
        editing['user].tell("The error was " + traceback()[1][2]);
    }
    return editing;
.



parent $in_location
object $lost_and_found

var $root objname 'lost_and_found
var $root created_on 806811775
var $root inited 1
var $root flags ['parameters, 'methods, 'code, 'core]
var $has_commands shortcuts #[]
var $has_commands remote #[]
var $has_commands local #[]
var $command_cache shortcut_cache 0
var $command_cache remote_cache 0
var $command_cache local_cache 0
var $named name ['prop, "Lost and Found box", "Lost and Found box"]
var $named name_aliases []
var $gendered gender $gender_neuter
var $messaged verbs #[]
var $described prose []
var $location contents []
var $located location $void
var $located obvious 1
var $thing lock <$object_lock_frob, [$void]>



parent $cml2_format
object $cml2_telnet_format

var $root objname 'cml2_telnet_format
var $root created_on 806313932
var $root inited 1
var $root flags ['parameters, 'methods, 'code, 'core]

public init
    return #[['indent, 0]];
.

public do_ul
    arg vars, flags, args;
    var ret, out, header, iline, line, token;
    
    vars = vars.add('indent, vars['indent] + 1);
    iline = "  ".repeat(vars['indent] + 1);
    
    // + "* ";
    line = iline;
    out = "\\n\\n";
    while (args) {
        token = args[1];
        args = args.subrange(2);
        switch (type(token)) {
            case 'frob:
                switch (class(token)) {
                    case $ctext_format:
                        switch (token.name()) {
                            case "lh":
                                ret = ._eval_ctext(vars, token.args());
                                out = out + ret[1];
                                vars = ret[2];
                            case "li":
                                ret = ._eval_ctext(vars, token.args());
                                out = out + iline + ret[1] + "\\n";
                                vars = ret[2];
                        }
                    case $ctext_generator:
                        ret = vars['evaluator]._eval_ctext(vars, token);
                        args = [@ret[1], @args];
                        vars = ret[2];
                }
        }
    }
    vars = vars.add('indent, vars['indent] - 1);
    out = out + "\\n";
    return [out, vars];
.

public do_strong
    arg vars, flags, args;
    var out, ret, a;
    
    ret = ._eval_ctext(vars, args);
    return [ret[1].uppercase(), ret[2]];
.

public do_dl
    arg vars, flags, args;
    var out, indent, ret, token;
    
    indent = vars['indent];
    vars = vars.add('indent, indent = 1);
    vars = vars.add('compact, (| flags["compact"] |) || 0);
    out = "\\n";
    for token in (args) {
        if (type(token) == 'frob && class(token) == $ctext_format && token.name() in ["dl", "lh", "dd", "dt"]) {
            ret = ._eval_ctext(vars, token);
            out = out + ret[1];
            vars = ret[2];
        }
    }
    vars = vars.add('indent, indent);
    return [out + "\\n", vars];
.

public do_anchor
    arg vars, flags, args;
    var ret;
    
    ret = ._eval_ctext(vars, [args[1]]);
    return [["/" + ret[1][1] + "/"], ret[2]];
.

public do_p
    arg vars, args, flags;
    
    return ["\\n\\n", vars];
.

public do_em
    arg vars, flags, args;
    var out, ret, a;
    
    ret = ._eval_ctext(vars, args);
    return [ret[1].uppercase(), ret[2]];
.

public do_br
    arg vars, args, flags;
    
    return ["\\n", vars];
.

public do_bold
    arg vars, flags, args;
    var out, ret, a;
    
    ret = ._eval_ctext(vars, args);
    return [ret[1].uppercase(), ret[2]];
.

public do_link
    arg vars, flags, args;
    var ret;
    
    ret = ._eval_ctext(vars, [args[1]]);
    return ["/" + ret[1] + "/", ret[2]];
.

public do_web
    arg vars, flags, args;
    var ret;
    
    ret = ._eval_ctext(vars, [args[1]]);
    return [ret[1] + "<" + flags["name"] + ": " + flags["src"] + ">", ret[2]];
.

public do_hr
    arg vars, flags, args;
    
    return ["\\n---\\n", vars];
.

public do_tt
    arg vars, flags, args;
    var out, ret, a;
    
    ret = ._eval_ctext(vars, args);
    args = ret[1];
    vars = ret[2];
    return ["`" + args + "`", vars];
.

public do_action
    arg vars, flags, args;
    var ret;
    
    ret = ._eval_ctext(vars, args[1]);
    (| vars['receiver].register_action(ret[1], vars['this], flags["token"]) |);
    return ["\\" + ret[1] + "\\", ret[2]];
.

public do_subj
    arg vars, flags, args;
    var out, ret, word, l;
    
    ret = ._eval_ctext(vars, args);
    out = ret[1];
    switch (toint((| flags["level"] |) || "4")) {
        case 1:
            out = "\\n" + out.uppercase() + "".pad(out.length(), "=") + "\\n";
        case 2:
            out = "\\n\\n" + out + "\\n" + "".pad(out.length(), "-");
        default:
            out = "\\n" + out;
    }
    return [out, ret[2]];
.

public do_dd
    arg vars, flags, args;
    var ret;
    
    vars = vars.add('indent, vars['indent] + 1);
    ret = ._eval_ctext(vars, args);
    vars = vars.add('indent, vars['indent] - 1);
    if (vars['compact])
        return [ret[1], vars];
    else
        return [ret[1].wrap_line(74, "     ", 1) + "\\n", vars];
.

public do_b
    arg vars, flags, args;
    var out, ret, a;
    
    ret = ._eval_ctext(vars, args);
    args = ret[1];
    vars = ret[2];
    out = [];
    for a in (args)
        out = [@out, a.uppercase()];
    return [out, vars];
    
    // 8-23-95/17:04 Jeff ($jeff), moved from $cml2_telnet_format.do_bold
.

public do_i
    arg vars, flags, args;
    var out, ret, a;
    
    ret = ._eval_ctext(vars, args);
    args = ret[1];
    vars = ret[2];
    out = [];
    for a in (args)
        out = [@out, a.uppercase()];
    return [out, vars];
    
    // 8-23-95/17:04 Jeff ($jeff), moved from $cml2_telnet_format.do_em
.

public do_dt
    arg vars, flags, args;
    var ret, term;
    
    term = "  ".repeat(vars['indent]);
    ret = ._eval_ctext(vars, args);
    if (vars['compact])
        return ["\\n" + (term + ret[1]).pad(25), ret[2]];
    else
        return ["\\n" + term + ret[1] + "\\n", ret[2]];
.

public do_li
    arg vars, flags, args;
    var line, ret;
    
    line = ["  ".repeat(vars['indent] + 1) + "* "];
    ret = ._eval_ctext(vars, args);
    return [[@line.join(@ret[1]), ""], ret[2]];
.



parent $cml2_format
object $cml2_html_format

var $root objname 'cml2_html_format
var $root created_on 811463041
var $root inited 1
var $root flags ['parameters, 'methods, 'code, 'core]

public do_hr
    arg vars, flags, args;
    
    return ["\\n<hr>\\n", vars];
.

public do_ul
    arg vars, flags, args;
    var ret, out, header, line, token;
    
    ret = ._eval_ctext(vars, args);
    return ["\\n<ul>\\n" + ret[1] + "\\n</ul>\\n", ret[2]];
.

public do_strong
    arg vars, flags, args;
    var out, ret, a;
    
    ret = ._eval_ctext(vars, args);
    return ["<strong>" + ret[1] + "</strong>", ret[2]];
.

public do_dl
    arg vars, flags, args;
    var out, ret, token;
    
    vars = vars.add('compact, (| flags["compact"] |) || 0);
    if ((| flags["compact"] |))
        out = "\\n<dl compact>";
    else
        out = "\\n<dl>";
    for token in (args) {
        if (type(token) == 'frob && class(token) == $ctext_format && token.name() in ["dl", "lh", "dd", "dt"]) {
            ret = ._eval_ctext(vars, token);
            out = out + ret[1];
            vars = ret[2];
        }
    }
    return [out + "\\n</dl>", vars];
.

public do_anchor
    arg vars, flags, args;
    var ret;
    
    ret = ._eval_ctext(vars, [args[1]]);
    return [["/" + ret[1][1] + "/"], ret[2]];
.

public do_p
    arg vars, args, flags;
    
    return ["\\n<p>\\n", vars];
.

public do_em
    arg vars, flags, args;
    var out, ret, a;
    
    ret = ._eval_ctext(vars, args);
    return [ret[1].uppercase(), ret[2]];
.

public do_br
    arg vars, args, flags;
    
    return ["\\n<br>", vars];
.

public do_bold
    arg vars, flags, args;
    var out, ret, a;
    
    ret = ._eval_ctext(vars, args);
    return [ret[1].uppercase(), ret[2]];
.

public do_link
    arg vars, flags, args;
    var ret, node;
    
    ret = ._eval_ctext(vars, [args[1]]);
    node = (| flags["node"] |);
    if (node)
        return ["<a href=\"/bin/help?" + node + "\">" + ret[1] + "</a>", ret[2]];
    return ["&gt;&gt;ERROR: Invalid node&lt;&lt;", ret[2]];
.

public do_web
    arg vars, flags, args;
    var ret;
    
    ret = ._eval_ctext(vars, [args[1]]);
    return [ret[1] + "<a href=\"" + flags["src"] + "\">" + flags["name"] + "</a>", ret[2]];
.

public do_tt
    arg vars, flags, args;
    var out, ret, a;
    
    ret = ._eval_ctext(vars, args);
    args = ret[1];
    vars = ret[2];
    return ["<tt>`" + args + "`</tt>", vars];
.

public do_action
    arg vars, flags, args;
    var ret;
    
    ret = ._eval_ctext(vars, args[1]);
    (| vars['receiver].register_action(ret[1], vars['this], flags["token"]) |);
    return ["\\" + ret[1] + "\\", ret[2]];
.

public do_subj
    arg vars, flags, args;
    var out, ret, word, l;
    
    ret = ._eval_ctext(vars, args);
    
    // our levels are kindof different from html's levels, so shift them
    l = tostr(abs(toint((| flags["level"] |) || 2)) || 1);
    return ["<h" + l + ">" + ret[1] + "</h" + l + ">\\n", ret[2]];
.

public do_dd
    arg vars, flags, args;
    var ret;
    
    ret = ._eval_ctext(vars, args);
    return ["<dd>" + ret[1], vars];
.

public do_b
    arg vars, flags, args;
    var out, ret, a;
    
    ret = ._eval_ctext(vars, args);
    return ["<b>" + ret[1] + "</b>", ret[2]];
.

public do_i
    arg vars, flags, args;
    var out, ret, a;
    
    ret = ._eval_ctext(vars, args);
    return ["<i>" + ret[1] + "</i>", ret[2]];
.

public do_dt
    arg vars, flags, args;
    var ret, term;
    
    ret = ._eval_ctext(vars, args);
    return ["\\n<dt>" + ret[1], ret[2]];
.

public do_li
    arg vars, flags, args;
    var ret;
    
    ret = ._eval_ctext(vars, args);
    return ["<li>" + ret[1], ret[2]];
.

parent $root_evaluator
object $compile_evaluator

var $root fertile 1
var $root manager $control
var $root inited 1
var $root objname 'compile_evaluator
var $root created_on 796268969
var $root flags ['parameters, 'methods, 'code, 'fertile, 'core]

public compile_cml
    arg text;
    var output, char, mode, vars;
    
    //Turn the given text into the ctext format.
    if (type(text) == 'list)
        text = text.to_string("").onespace();
    if (type(text) != 'string)
        throw(~type, "Text should be a string.");
    text = text + "\"";
    if (text[1] == "\"")
        text = text.subrange(1);
    vars = #[['this, sender()]];
    vars = .parse_string(vars, text);
    return vars;
.

public parse_string
    arg vars, text;
    var output, word, is_text, token, ret_val;
    
    output = [];
    word = "";
    is_text = 0;
    while (text) {
        ret_val = text.split_on_next(["\"", "{", "%", "&"]);
        word = ret_val[1];
        token = ret_val[2];
        text = ret_val[3];
        switch (token) {
            case "{":
                output = ._handle_word(output, word);
                vars = .parse_command(vars, text);
                output = [@output, vars['result]];
                text = vars['text];
                is_text = 1;
            case "%":
                output = ._handle_word(output, word);
                vars = .parse_varref(vars, text);
                output = [@output, vars['result]];
                text = vars['text];
                is_text = 1;
            case "&":
                output = ._handle_word(output, word);
                vars = .parse_charref(vars, text);
                output = [@output, vars['result]];
                text = vars['text];
                is_text = 1;
            case "\"":
                output = ._handle_word(output, word);
                if (is_text) {
                    vars = .text_stmt(vars, output);
                    output = vars['result];
                }
                if (output.length() == 1)
                    output = output[1];
                vars = vars.add('text, text);
                return vars.add('result, output);
        }
    }
    vars = vars.add('text, text);
    return vars.add('result, output);
.

public parse_command
    arg vars, text;
    var output, ret_val, term, word, final, first_term, final_args, token;
    
    output = [];
    term = [];
    word = "";
    while (text) {
        ret_val = text.split_on_next(["\"", "{", "%", "&", "[", " ", "}"]);
        word = ret_val[1];
        token = ret_val[2];
        text = ret_val[3];
        switch (token) {
            case "{":
                output = ._handle_word(output, term);
                vars = .parse_command(vars, text);
                output = [@output, vars['result]];
                text = vars['text];
            case "[":
                output = ._handle_word(output, term);
                vars = .parse_list(vars, text);
                output = [@output, vars['result]];
                text = vars['text];
            case "%":
                output = ._handle_word(output, term);
                vars = .parse_varref(vars, text);
                output = [@output, vars['result]];
                text = vars['text];
            case "\"":
                output = ._handle_word(output, term);
                vars = .parse_string(vars, text);
                output = [@output, vars['result]];
                text = vars['text];
            case " ":
                if (word == "pre" && !output) {
                    ret_val = ")" in text;
                    output = [['string_type, "pre"], ['string_type, text.subrange(1, ret_val - 1)]];
                    text = text.subrange(ret_val);
                } else {
                    output = ._handle_word(output, word);
                }
            case "}":
                output = ._handle_word(output, word);
    
                //       if (type(output[1]) != 'list)
                //         output = [output];
                first_term = output[1];
                if (first_term[1] == 'string_type) {
                    first_term = tosym(first_term[2] + "_stmt");
                    final_args = output.subrange(2);
                    catch ~methodnf {
                        vars = .(first_term)(vars, final_args);
                    } with handler {
                        vars = vars.add('result, [first_term, final_args]);
                    }
                } else if (type(first_term[1]) == 'symbol) {
                    final_args = [@first_term[2], @output.subrange(2)];
                    first_term = first_term[1];
                    vars = vars.add('result, [first_term, final_args]);
                } else {
                    first_term = 'eval_stmt;
                    final_args = [first_term];
                    vars = vars.add('result, [first_term, final_args]);
                }
                vars = vars.add('text, text);
                return vars;
        }
    }
    vars = vars.add('text, "");
    return vars.add('result, ['string_type, "unended command"]);
.

public parse_charref
    arg vars, text;
    var pos;
    
    pos = ";" in text;
    vars = vars.add('text, text.subrange(pos + 1));
    text = text.subrange(1, pos - 1);
    switch (text) {
        case "lp":
            text = "(";
        case "amp":
            text = "&";
        case "quote":
            text = "\"";
        case "per":
            text = "%";
        case "lb":
            text = "{";
        case "rb":
            text = "}";
        case "tick":
            text = "'";
        default:
            text = ">>ERROR: Unknown character " + text + "<<";
    }
    return vars.add('result, ['string_type, text]);
.

public parse_varref
    arg vars, text;
    var output, ret_val, word, token;
    
    output = [];
    word = "";
    while (text) {
        ret_val = text.split_on_next(["{", "%", "&", ";"]);
        word = ret_val[1];
        token = ret_val[2];
        text = ret_val[3];
        switch (token) {
            case "{":
                output = ._handle_word(output, word);
                ret_val = .parse_command(vars, text);
                output = [@output, vars['result]];
                text = vars['text];
            case "%":
                output = ._handle_word(output, word);
                ret_val = .parse_varref(vars, text);
                output = [@output, vars['result]];
                text = vars['text];
            case "&":
                output = ._handle_word(output, word);
                ret_val = .parse_charref(vars, text);
                output = [@output, vars['result]];
                text = vars['result];
            case ";":
                output = ._handle_word(output, word);
                vars = vars.add('text, text);
                return vars.add('result, ['get_stmt, output]);
        }
    }
.

public parse_list
    arg vars, text;
    var output, term, word, token, int, ret_val;
    
    output = [];
    term = [];
    word = "";
    while (text) {
        ret_val = text.split_on_next(["[", "]", "{", "\"", "%", "&", " "]);
        word = ret_val[1];
        token = ret_val[2];
        text = ret_val[3];
        switch (token) {
            case "{":
                output = ._handle_word(output, word);
                vars = .parse_command(vars, text);
                output = [@output, vars['result]];
                text = vars['text];
            case "[":
                output = ._handle_word(output, word);
                vars = .parse_list(vars, text);
                output = [@output, vars['result]];
                text = vars['text];
            case "%":
                output = ._handle_word(output, word);
                vars = .parse_varref(vars, text);
                output = [@output, vars['result]];
                text = vars['text];
            case "\"":
                output = ._handle_word(output, word);
                vars = .parse_string(vars, text);
                output = [@output, vars['text]];
                text = vars['text];
            case " ":
                output = ._handle_word(output, word);
            case "]":
                output = ['list_type, ._handle_word(output, word)];
                vars = vars.add('text, text);
                return vars.add('result, output);
        }
    }
.

public _handle_word
    arg output, word;
    
    if (word) {
        if (type(word) == 'string) {
            if (word.is_numeric()) {
                word = ['integer_type, toint(word)];
            } else if (word[1] == "$") {
                if (" " in word)
                    word = ['string_type, word];
                else
                    word = ['dbref_type, $object_lib.to_dbref(word)];
            } else if (word[1] == "'") {
                word = ['symbol_type, tosym(word)];
            } else {
                word = ['string_type, word];
            }
        }
        output = [@output, word];
    }
    return output;
.

public char_type
    arg vars, args;
    
    if (args[1] != 'string_type)
        return ['string_type, ">>ERROR: Character must be a constant string."];
    switch (args) {
        case "lp":
            return ['string_type, "("];
        case "amp":
            return [string_type, "&"];
        case "quote":
            return ['string_type, "\""];
        case "per":
            return ['string_type, "%"];
        default:
            return ['string_type, ">>ERROR: Unknown character " + args + "<<"];
    }
.

public text_stmt
    arg vars, args;
    var last, output, term, first;
    
    output = [];
    for term in (args) {
        if (output) {
            last = output.last();
            if (term[1] == 'string_type && last[1] == 'string_type) {
                term = ['string_type, last[2] + term[2]];
                output = output.chop();
            }
        }
        output = [@output, term];
    }
    if (output.length() == 1) {
        first = output[1];
        if (first[1] == 'string_type)
            return vars.add('result, first);
    }
    return vars.add('result, ['text_stmt, output]);
.

public action_stmt
    arg vars, args;
    
    return vars.add('result, ['action_stmt, [vars['this], @args]]);
.