object $root;

var $root manager = $root;
var $root owners = [$root];
var $root trusted = [];
var $root owned = [$root];
var $root child_index = 13;
var $root fertile = 1;
var $root inited = 0;
var $root quota = 75000;
var $root managed = 0;
var $root writers = [$root];
var $root created_on = 0;
var $root flags = ['methods, 'code, 'fertile, 'core, 'variables];
var $root quota_exempt = 0;
var $root writes = 0;
var $root trusted_by = 0;
var $root help_node = 0;

root method .init_root() {
    .change_manager(this());
    flags = ['variables, 'methods, 'code];
    created_on = time();
    .add_owner(this());
};

root method .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 method .initialize(): nooverride {
    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 method .uninitialize(): nooverride {
    var ancestor, errors, p;
    
    (> .perms(caller(), $root, $sys) <);
    errors = [];
    for ancestor in (ancestors()) {
        catch any
            ._uninitialize_ancestor(ancestor);
        with
            errors = [@errors, traceback()];
    }
    for p in (.variables())
        .del_var(p);
    return errors;
};

public method .perms(): nooverride {
    arg what, [args];
    var flag;
    
    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) + " is not this.", what);
            case 'system:
                if (!($sys.is_system(what)))
                    throw(~perm, ("Permission Denied: " + what) + " is not of the system.", what);
            case 'manager:
                if (((.manager()) != what) && (!($sys.is_system(what))))
                    throw(~perm, ("Permission Denied: " + what) + " is not the manager.", what);
            case 'trusts:
                if (!(.trusts(what)))
                    throw(~perm, ("Permission Denied: " + what) + " is not a trustee.", what);
            default:
                if (!(.is_writable_by(what)))
                    throw(~perm, ("Permission Denied: " + what) + " is not a writer.", what);
        }
    } else if (type(what) == 'objnum) {
        if (!(what in args))
            throw(~perm, (what + " is not one of: ") + ($list.to_english($list.mmap(args, 'name, 'ref))), what);
    }
    
    // $# Edited 29 Oct 1995 14:32 Lynx ($lynx)
    // $#Edited: 13 Jun 96 20:42 $levi
};

root method ._change_parents(): nooverride {
    arg parents;
    var old_a, old_p, p, o, a, new_a;
    
    if (!parents)
        throw(~noparents, "Objects must have at least 1 parent");
    
    // remember the old ancestors and parents
    old_a = ancestors();
    old_p = .parents();
    
    // build the new ancestors list by hand, so we can uninit befor changing
    new_a = [this(), @parents];
    for p in (parents)
        new_a = new_a.union(p.ancestors());
    
    // uninit it
    for a in (old_a.set_difference(new_a))
        (| ._uninitialize_ancestor(a) |);
    
    // do the change
    (> chparents(parents) <);
    
    // init new
    for a in (ancestors().set_difference(old_a))
        (| ._initialize_ancestor(a) |);
};

public method .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 method .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, ((this() + " refuses to be parent of ") + obj) + ".");
};

public method .manager(): nooverride {
    return manager || $control;
};

public method .owners(): nooverride {
    return owners || [];
};

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

public method .trusted(): nooverride {
    arg [literal];
    
    if (literal)
        return trusted || [];
    return (trusted || []) + (.writers());
};

public method .is_writable_by(): nooverride {
    arg obj;
    
    return (| obj in (.writers()) |) || ($sys.is_system(obj));
};

public method .trusts(): nooverride {
    arg obj;
    
    return (| obj in (.trusted()) |) || ((obj == $scheduler) || ($sys.is_system(obj)));
};

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

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

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

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

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

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

public method .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 method .add_parent() {
    arg parent;
    
    (> .perms(sender(), 'manager) <);
    if (parent in (.parents()))
        throw(~parent, ((this() + " already has ") + parent) + " as a parent.");
    if (!valid(parent))
        throw(~type, "Not a valid parent, must send a dbref or pointer");
    .chparents(@.parents(), parent);
};

public method .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 + " is not a parent of ") + this()) + ".");
    parents = parents.setremove(parent);
    (> .chparents(@parents) <);
};

public method .spawn(): nooverride {
    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 = tostr((| .objname() |) || "unknown");
        tmp = tosym(objname);
        while ((| lookup(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;
    
    // $#Edited: 18 Jul 96 14:56 $levi
};

public method .destroy(): nooverride {
    // 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() <);
    destroy();
};

public method .add_var() {
    arg name, [args];
    var tmp, kid;
    
    (> .perms(sender()) <);
    if (!(tostr(name).valid_ident()))
        throw(~invident, name + " is not a valid ident.");
    (> add_var(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)) + "_") + time());
        catch any {
            add_method([((tostr(name) + " = ") + toliteral(args[1])) + ";"], tmp);
            .(tmp)();
            if (((args.length()) > 1) && ((args[2]) == 'inherit)) {
                for kid in (.descendants())
                    kid.(tmp)();
            }
            del_method(tmp);
        } with {
            del_method(tmp);
            rethrow(error());
        }
    }
    
    // $#Edited: 08 Jan 96 09:34 Lynx ($lynx)
};

public method .variables(): nooverride {
    return variables();
};

public method .del_var(): nooverride {
    arg name;
    var n, obj;
    
    (caller() == definer()) || (> .perms(sender()) <);
    
    // try and clear the variable on all of the descendants, before deleting
    // the variable...
    (> .clear_variable(name) <);
    
    // now delete the variable
    (> del_var(name) <);
};

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

public method .methods(): nooverride {
    if (!(.has_flag('methods, sender())))
        throw(~perm, (sender() + " doesn't have permission to find methods on ") + this());
    return methods();
};

public method .parents(): nooverride {
    return parents();
};

public method .children(): nooverride {
    return children();
};

public method .ancestors(): nooverride {
    return ancestors();
};

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

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

public method .add_method() {
    arg code, name;
    
    (> .perms(sender()) <);
    (| $sys.touch() |);
    return (> add_method(code, name) <);
};

public method .has_ancestor(): nooverride {
    arg obj;
    
    return (> has_ancestor(obj) <);
};

public method .eval(): nooverride {
    arg code, [dest];
    var errors, result, method;
    
    dest = dest ? dest[1] : this();
    if (!(sender() in [$scheduler, $root]))
        (> .perms(sender()) <);
    
    // Compile the code.
    method = tosym("tmp_eval_" + tostr(time()));
    errors = .add_method(code, method);
    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.(method)() <);
    } with {
        (| del_method(method) |);
        rethrow(error());
    }
    (| del_method(method) |);
    return ['result, result];
    
    // $#Edited: 06 Jan 96 14:58 Lynx ($lynx)
};

public method .method_info(): nooverride {
    arg [args];
    
    return (> method_info(@args) <);
};

public method .data(): nooverride {
    arg [parent];
    var par, data, out;
    
    if (!(.has_flag('variables, sender())))
        throw(~perm, ((sender().namef('ref)) + " is not allowed to read variables on ") + (.namef('ref)));
    if (parent) {
        if (type(parent[1]) != 'objnum)
            throw(~type, ($parse.unparse(parent[1])) + " is not an object.");
        return (> data(parent[1]) <);
    } else {
        data = (> 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('variables, sender())))
                out = out.add(par[1], par[2]);
            else
                out = out.add(par[1], ["*** Permission Denied ***"]);
        }
        return out;
    }
};

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

public method .descendants(): nooverride {
    var kids, i, c, child, d;
    
    d = #[];
    for child in (children())
        d = dict_add(d, child, 1);
    while ((| (c = dict_keys(d)[++i]) |)) {
        for child in (c.children())
            d = dict_add(d, child, 1);
        pause();
    }
    return dict_keys(d);
};

public method .get_quota(): nooverride {
    return quota;
};

public method .owned(): nooverride {
    return owned || [];
};

public method .quota_valid(): nooverride {
    if (quota_exempt)
        return 1;
    if (!(.is($user)))
        return 0;
    return (.quota_byte_usage()) < (.quota());
};

root method .del_owned_obj(): nooverride {
    if (owned) {
        owned = owned.setremove(sender());
        if (!owned)
            clear_var('owned);
    }
    
    // $#Edited: 30 Nov 95 08:29 Jenner ($jenner)
};

root method .add_owned_obj(): nooverride {
    owned = (owned || []).setadd(sender());
};

public method .match_children() {
    arg string;
    var children, child_names, c;
    
    children = .children();
    child_names = $list.mmap(children, 'name);
    
    // 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 method .del_flag(): nooverride {
    arg flag;
    
    (> .perms(sender(), 'manager) <);
    
    // let them add any flag they want
    flags = (flags || []).setremove(flag);
};

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

public method ._get_method_info(): nooverride {
    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 method .namef() {
    arg type;
    
    return tostr(this());
    
    // $# Edited 28 Oct 1995 21:06 Lynx ($lynx)
};

public method .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 method .chown(): nooverride {
    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 method .debug() {
    arg [stuff];
    var x, line, mngr, meth;
    
    meth = (| (stack()[2])[3] |);
    if (meth)
        line = ((("DEBUG " + sender()) + ".") + meth) + "(): ";
    else
        line = ("DEBUG " + sender()) + ": ";
    for x in (stuff)
        line = (line + " ") + toliteral(x);
    (| (.manager()).tell(line) |);
    
    // $#Edited: 13 Jun 96 01:13 $levi
};

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

public method .name() {
    arg [args];
    
    return tostr(this());
};

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

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

public method .quota() {
    return quota;
};

public method .quota_byte_usage() {
    var total, obj;
    
    // perm checking goes here
    for obj in (.owned()) {
        if (!valid(obj))
            continue;
        total += obj.size();
    }
    return total;
};

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

root method ._uninitialize_ancestor() {
    arg ancestor;
    var method, found;
    
    if (!(method = tosym("uninit_" + tostr(ancestor.objname()))))
        return;
    catch ~methodnf {
        if ((.find_method(method)) == ancestor) {
            found = 1;
            .(method)();
        }
        (| $root.clear_def_vars(ancestor) |);
        if (!found)
            throw(~perm, ((("UnInitialization method for " + ancestor) + " in wrong place (") + (.find_method(method))) + ")");
    }
    
    // $#Edited: 08 Jan 96 09:27 Lynx ($lynx)
};

public method .objname() {
    return (> objname() <);
};

public method .set_objname() {
    arg objname;
    
    // Only accept calls from owners or admins.
    (> .perms(sender()) <);
    
    // Make sure first argument is a symbol.
    if (type(objname) != 'symbol)
        throw(~type, "New objname is not a symbol.");
    
    // Make sure everything is lowercase.
    objname = tosym(tostr(objname).lowercase());
    
    // Do nothing if objname isn't different.
    if (objname == (| objname() |))
        return;
    return (> set_objname(objname) <);
    
    // $#Edited: 05 Mar 96 12:33 Lynx ($lynx)
};

public method .del_objname() {
    return (> del_objname() <);
};

public method .set_created() {
    arg value;
    
    created_on = value;
};

public method .created_on() {
    return created_on;
};

public method .is_of(): nooverride {
    arg obj;
    
    return obj in ancestors();
};

public method .is(): nooverride {
    arg [args];
    
    if (!args)
        throw(~invarg, "Must have one argument.");
    if (type(args[1]) == 'dictionary)
        return has_ancestor(args[2]);
    return has_ancestor(args[1]);
};

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

public method .add_flag(): nooverride {
    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 method .build_name() {
    arg [args];
    var output, type, part, rval;
    
    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;
    
    // $# Edited 28 Oct 1995 20:57 Lynx ($lynx)
};

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

public method .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 method .descendants_to(): nooverride {
    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 method .ancestry() {
    arg gen;
    var i, out;
    
    out = [];
    if (type(gen) == 'objnum) {
        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 method .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 method .variable_info() {
    arg gen, def, fltr, match;
    var ancs, data, pp, p;
    
    if (!(.has_flag('variables, sender())))
        throw(~perm, (sender() + " cannot read variables on ") + this());
    if (def)
        ancs = [def];
    else
        ancs = .(gen[1])(gen[2]) || [this()];
    data = [];
    for pp in ($list.reverse(data().to_list())) {
        if (valid(pp[1]) && ((pp[1]) in ancs)) {
            for p in (pp[2]) {
                if (tostr(p[1]).(match)(fltr))
                    data = [@data, [pp[1], @p]];
            }
        }
    }
    return data;
};

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

public method .method_access(): nooverride {
    arg method;
    
    if (!(.has_flag('methods, sender())))
        throw(~perm, (sender() + " doesn't have permission to find methods on ") + this());
    return (> method_access(method) <);
};

public method .set_method_flags(): nooverride {
    arg method, flags;
    
    if (!(.is_writable_by(sender())))
        throw(~perm, (sender() + " cannot write to ") + this());
    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 method .set_method_access(): nooverride {
    arg method, state;
    
    if (!(.is_writable_by(sender())))
        throw(~perm, (sender() + " cannot write to ") + this());
    return (> set_method_access(method, state) <);
};

public method .set_flags(): nooverride {
    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 method .managed(): nooverride {
    return managed || [];
};

root method .add_managed_obj(): nooverride {
    managed = (managed || []).setadd(sender());
};

root method .del_managed_obj(): nooverride {
    managed = (managed || []).setremove(sender());
    if (!managed)
        clear_var('managed);
};

public method .writes(): nooverride {
    (> .perms(sender(), 'trusts) <);
    return writes || [];
};

root method .add_writable_obj(): nooverride {
    writes = (writes || []).setadd(sender());
};

root method .del_writable_obj(): nooverride {
    writes = (writes || []).setremove(sender());
    if (!writes)
        clear_var('writes);
};

public method .trusted_by(): nooverride {
    return trusted_by || [];
};

root method .add_trusted_obj(): nooverride {
    trusted_by = (trusted_by || []).setadd(sender());
};

root method .del_trusted_obj(): nooverride {
    trusted_by = (trusted_by || []).setremove(sender());
    if (!trusted_by)
        clear_var('trusted_by);
};

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

public method .flags(): nooverride {
    return flags || [];
};

public method .quota_exempt(): nooverride {
    return quota_exempt;
};

public method .clean_root() {
    var obj;
    
    // Called by $sys.clean_database()
    (> .perms(caller(), $sys) <);
    (| ._clean_root('trusted, 'trusted_by) |);
    (| ._clean_root('trusted_by, 'trusted) |);
    (| ._clean_root('writers, 'writes) |);
    (| ._clean_root('writes, 'writers) |);
    (| ._clean_root('owners, 'owned) |);
    (| ._clean_root('owned, 'owners) |);
    if (!manager) {
        manager = this();
        .change_manager($reaper);
    }
    if (managed) {
        managed = managed.valid_objects();
        for obj in (managed) {
            refresh();
            if ((obj.manager()) != this())
                managed = setremove(managed, obj);
        }
        if (!managed)
            clear_var('managed);
    }
};

public method .objnum() {
    return objnum();
    
    // $#Edited: 06 Jan 96 13:28 Lynx ($lynx)
};

root method .clear_def_vars(): nooverride {
    arg definer;
    var code, v, a, meth, errs;
    
    code = [];
    for v in (definer.variables())
        code = code + [("clear_var(" + v) + ");"];
    meth = tosym("_root_tmp_" + time());
    if (!(definer.add_method(code, meth))) {
        (| sender().(meth)() |);
        (| definer.del_method(meth) |);
    }
    
    // $#Edited: 08 Jan 96 09:10 Lynx ($lynx)
};

public method .list_methods() {
    arg gen, def, fltr, match;
    var ancs, a, m, i, methods;
    
    if (!(.has_flag('methods, sender())))
        throw(~perm, (sender() + " cannot view methods on ") + this());
    if (def)
        ancs = [def];
    else
        ancs = .(gen[1])(gen[2]) || [this()];
    methods = #[];
    for a in (ancs) {
        for m in (a.methods()) {
            if (tostr(m).(match)(fltr)) {
                i = a.method_info(m);
                methods = methods.add_elem(i[5], [a, m, @i]);
            }
        }
    }
    return methods;
};

public method .method_bytecode() {
    arg method;
    
    return (> method_bytecode(method) <);
};

public method .rename_method() {
    arg now, new;
    
    (> .perms(sender()) <);
    (| $sys.touch() |);
    return (> rename_method(now, new) <);
};

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

public method .examine() {
    return [];
};

public method .new_descendants() {
    var kid, kids;
    
    kids = #[];
    for kid in (children()) {
        kids = kids.add(kid, 1);
        kids = kids.union(kid.new_descendants());
        pause();
    }
    return kids.keys();
    
    // $#Edited: 30 Jun 96 23:06 $jenner
};

private method ._clean_root() {
    arg v, m;
    var obj, value;
    
    if ((value = get_var(v))) {
        value = value.valid_objects();
        for obj in (value) {
            if (!(this() in obj.(m)()))
                value = value.setremove(obj);
            refresh();
        }
        if (value)
            set_var(v, value);
        else
            clear_var(v);
    }
};

private method .set_quota_exempt(): nooverride {
    arg bool;
    
    if (bool)
        quota_exempt = 1;
    else if (quota_exempt)
        (| clear_var('quota_exempt) |);
};

public method .help_node() {
    return help_node;
    
    // $#Edited: 30 Jul 96 16:45 $jenner
};

public method .set_hnode() {
    arg node;
    
    (> .perms(sender(), 'manager) <);
    help_node = node;
    
    // $#Edited: 30 Jul 96 16:46 $jenner
};

public method .dbref() {
    return (| objname() |) || toliteral(this());
};

public method .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) <);
};


object $sys: $root;

var $sys admins = [];
var $sys agents = [$root, $daemon];
var $sys startup = #[['time, 843407414], ['objects, [$login_daemon, $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 = ["129.123.4.76", "ice.cold.org"];
var $sys system_email_addresses = #[['default, "brandon@cold.org"]];
var $sys core_version = "3.0";
var $sys validate_email_addresses = 1;
var $sys backup = #[['interval, 3600], ['last, 843406948], ['next, 843411014]];
var $sys system = [$sys, $root];
var $sys touched = 843407456;
var $sys loggers = [$http_interface, $daemon, $user, $connection];
var $sys core_date = 0;
var $sys dumped = [$root, $sys];
var $root created_on = 796268969;
var $root inited = 1;
var $root flags = ['methods, 'code, 'core, 'variables];
var $root quota_exempt = 1;
var $root manager = $sys;
var $sys bindings = #[['atomic, $sys], ['create, $sys], ['backup, $sys], ['shutdown, $sys], ['set_heartbeat, $sys], ['cancel, $sys], ['task_info, $scheduler], ['execute, $sys], ['bind_function, $sys], ['unbind_function, $sys], ['bind_port, $daemon], ['unbind_port, $daemon], ['open_connection, $connection], ['reassign_connection, $daemon], ['fopen, $file], ['fstat, $file], ['fchmod, $file], ['fmkdir, $file], ['frmdir, $file], ['files, $file], ['fremove, $file], ['frename, $file], ['fclose, $file], ['fseek, $file], ['feof, $file], ['fwrite, $file], ['fread, $file], ['fflush, $file], ['chparents, $root], ['destroy, $root], ['dblog, $sys], ['add_var, $root], ['del_var, $root], ['variables, $root], ['list_method, $root], ['add_method, $root], ['del_method, $root], ['method_bytecode, $root], ['methods, $root], ['rename_method, $root], ['set_method_access, $root], ['set_method_flags, $root], ['data, $root], ['del_objname, $root], ['set_objname, $root], ['suspend, $scheduler], ['resume, $scheduler]];
var $root managed = [$sys];
var $root owners = [$sys];
var $root owned = [$sys];

public method .next_objnum(): native;

public method .version(): native;

driver method .startup() {
    arg args;
    var opt, str, obj, f;
    
    if (args && ("-clean" in args)) {
        catch any
            (> .clean_database() <);
        with
            .log($parse_lib.traceback(traceback()));
        shutdown();
        return;
    }
    catch any {
        // Bind functions for security
        for f in (bindings) {
            catch any
                bind_function(@f);
            with
                dblog((("** Unable to bind function " + (f[1])) + "() to ") + (f[2]));
        }
    
        // 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) + ".startup()");
            catch any
                (> obj.startup(@args) <);
            with
                .log($parse_lib.traceback(traceback()));
        }
    } with {
        .log(("Startup ERROR at " + ctime()) + ":");
        .log(toliteral(traceback()));
        .log($parse_lib.traceback(traceback(), -1, ""));
    }
    
    // $#Edited: 21 Sep 96 12:24 $brandon
};

public method .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 'last_backup:
            return backup['last];
        case 'driver_version:
            tmp = .version();
            return (((((long ? "ColdC Genesis " : "") + (tmp[1])) + ".") + (tmp[2])) + "p") + (tmp[3]);
        case 'core_version:
            return (long ? "ColdCore " : "") + core_version;
        default:
            throw(~unknown, "Unknown flag.");
    }
};

public method .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);
        user.change_manager(user);
        user.set_data('email, email, 0);
        user.chown(user);
    } with {
        // Failed to initialize the child; destroy it.
        if (!(| user.destroy() |)) {
            (| user.uninitialize() |);
            (| user.destroy() |);
        }
        rethrow(error());
    }
    return user;
};

public method .admins() {
    return admins;
};

public method .is_admin() {
    arg obj;
    
    return (obj == $sys) || (obj in admins);
};

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

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

public method .is_system() {
    arg obj;
    
    return obj in system;
};

public method .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 {
        dblog((("[" + ($time.format("%d %h %y %H:%M"))) + "] ") + text);
    }
};

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

public method .do_backup() {
    arg [args];
    var line, who, how, name, 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 started at " + ($time.ltime())) + " by ") + name;
        $channels.announce('System, line);
    }
    
    // double pause will hopefully let people know about it before it happens
    pause();
    pause();
    backup = backup.add('started, time());
    (> backup() <);
};

public method .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 method .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 method .new_admin() {
    if (caller() != $admin)
        throw(~perm, "Caller is not $admin.");
    admins = admins.setadd(sender());
};

public method .agents() {
    return agents;
};

public method .get_startup() {
    arg what;
    
    return starting[what];
};

public method .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 method .execute() {
    arg script, args, [background];
    
    (> .perms(sender(), 'system) <);
    (> execute(script, args, @background) <);
};

public method .get_starting() {
    arg what;
    
    return starting[what];
};

public method .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 {
        // Failed to initialize the child; destroy it.
        if (!(| new.destroy() |)) {
            (| new.uninitialize() |);
            (| new.destroy() |);
        }
        rethrow(error());
    }
    return new;
};

public method .system() {
    return system;
};

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

public method .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 method .add_method() {
    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 method .validate_email_addresses() {
    return validate_email_addresses;
};

public method .reassign_connection() {
    arg obj;
    
    (> .perms(caller(), $network) <);
    (> reassign_connection(obj) <);
};

driver method .signal() {
    arg signal;
    var line;
    
    line = ("** Caught Signal: " + signal) + " **";
    (| $channels.announce('System, line) |);
    if (sig in ['QUIT, 'INT]) {
        (| $channels.announce('System, "******************************") |);
        (| $channels.announce('System, "** IMMINENT SERVER SHUTDOWN **") |);
        (| $channels.announce('System, "******************************") |);
    }
    
    // the driver will send the following signals:
    //   HUP  -- driver will drop out of atomic mode and will flush i/o
    //   USR2 -- driver does nothing
    //   USR1 -- driver aborts all currently executing tasks, not suggested
    //   QUIT -- For both of these the driver will shutdown after current
    //   INT     tasks finish their current execution frame.  Note: do not
    //           preempt tasks at this point as they will not resume.
};

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

public method ._status(): native;

bind_native .status() ._status();

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

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

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

public method .clean_database() {
    var obj, p, c, cmd;
    
    (> .perms(caller()) <);
    
    // cleanup some of $root's messiness
    for obj in ($root.descendants()) {
        (| obj.clean_root() |);
        refresh();
    }
    
    // rehash all command caches
    for obj in ($has_commands.descendants()) {
        if ((| obj.is_command_cache() |))
            (| obj.purge_caches() |);
        refresh();
    }
    
    // incase people are logged in, give back their caches :)
    // this does not update their local caches, that is only
    // done when they login.
    for obj in ((| $user_db.connected() |) || []) {
        for p in ((| obj.parents() |) || [])
            (| p.cache_init() |);
        refresh();
    }
    
    // check user info (move'em home etc)
    for obj in ($user.descendants()) {
        if ((| (obj.home()) != (obj.location()) |))
            (| obj.move_to(obj.home()) |);
        refresh();
    }
    
    // validate all locations and location content's
    for obj in ($physical.descendants()) {
        (| obj.validate_contents() |);
        if (obj.has_ancestor($located)) {
            if ((!valid(obj.location())) || (!(obj in ((obj.location()).contents()))))
                obj.move_to((| obj.home() |) || $lost_and_found);
        }
        refresh();
    }
};

public method .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 method .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 method .touch() {
    touched = time();
};

public method .touched() {
    return touched;
};

public method .backup() {
    return backup;
};

public method .dirty() {
    return touched > (backup['last]);
};

public method ._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.add_method(code, cname);
        for obj in (objs) {
            (| obj.(cname)() |);
            pause();
        }
    }
    $root.del_method(cname);
    .log("-- Object Ownership pass completed --");
};

public method .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 method .core_date() {
    return core_date;
};

public method .loggers() {
    return loggers;
};

driver method .backup_done() {
    var time, line;
    
    catch any {
        line = "Backup completed, executing filesystem cleanup...";
        $channels.announce('System, line);
        pause();
        pause();
        .execute("backup", []);
    }
    catch any {
        time = time() - (backup['started]);
        line = "Backup completed, elapsed time ";
        $channels.announce('System, line + ($time.elapsed(time, 'long)));
    }
    backup = backup.del('started);
};

private method .make_core() {
    var obj, d, o, top, x, admin, tmp, name;
    
    // core rooms should be +core, and cant be destroyed
    // traverse the list inverseley, less unseen heirarchial shuffling
    d = $root.descendants();
    top = listlen(d);
    dblog(("** " + top) + " objects, destroying all non-core objects...");
    catch any {
        for x in [1 .. top] {
            obj = d[(top - x) + 1];
            (| obj.destroy() |);
            if (!valid(obj)) {
                dblog(tostr(obj));
            } else {
                obj.change_manager(obj);
                for o in (obj.writers('literal)) {
                    obj.del_writer(o);
                    refresh();
                }
                for o in ((obj.owners()).setremove(obj)) {
                    obj.del_owner(o);
                    refresh();
                }
                obj.add_owner(obj);
                if ((name = (| tosym("coreify_" + (obj.objname())) |)))
                    (| obj.(name)() |);
            }
            refresh();
        }
        dblog("** cleaning up interfaces and other random core stuff");
        starting = dict_add(starting, 'new_user_class, $admin);
        $login_interface.add_command("cr?eate <any>", 'create_cmd);
        $login_interface.rehash_caches();
        for obj in ($place.descendants())
            obj.set_realm($realm_of_creation);
        dblog("** cleaning database..");
        .clean_database();
        dblog("** shutting down..");
        shutdown();
    } with {
        dblog("traceback: " + traceback());
    }
};

public method .task_info() {
    arg [args];
    
    (> .perms(sender(), 'system) <);
    return (> task_info(@args) <);
    
    // $#Edited: 19 Sep 96 09:59 $brandon
};


new object $foundation: $root;

var $root child_index = 6;
var $root fertile = 1;
var $root trusted = [];
var $root inited = 1;
var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'fertile, 'core, 'variables];
var $root manager = $foundation;
var $root managed = [$foundation];
var $root owners = [$foundation];
var $root owned = [$foundation];

public method .configure() {
    arg set;
    
    // This is for post-creation configuration of a VR object.  It is used
    // to interactively configure the VR aspects and behaviour of an object.
    // It should be optional, any command hooking into confingure should check
    // for a -conf?igure option first (which would imply skipping configuration).
    //
    // Overriding methods should pass() first, giving priority to their
    // ancestors.  The argument 'set' is a dictionary with symbol keys
    // defining what has been set.  Use the definer's name + "_" + setting
    // as the name of the key ("name" on $named would be 'named_name).
    // If something is already in the set dictionary, do not re-set it
    // again (for instance, if the command hooking into configure accepted
    // the name of the object on it's command line it would put
    // 'named_name in the dictionary and $named.configure() would not prompt
    // for the name).
    //
    // "@skip" should always skip the setting, and not alter it.
    // if .prompt() returns 'aborted, throw ~abort.
    //
    (> .perms(sender()) <);
};


new object $event_handler: $foundation;

var $root inited = 1;
var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'core, 'variables];
var $root manager = $event_handler;
var $root managed = [$event_handler];
var $root owners = [$event_handler];
var $root owned = [$event_handler];

public method .event() {
    arg frob;
    
};

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


new object $has_settings: $foundation;

var $root child_index = 1;
var $root fertile = 1;
var $root manager = $has_settings;
var $root owners = [$has_settings];
var $root inited = 1;
var $root flags = ['methods, 'code, 'fertile, 'core, 'variables];
var $root created_on = 796268969;
var $has_settings settings = #[];
var $has_settings defined_settings = #[];
var $has_settings local_settings = [];
var $root managed = [$has_settings];
var $root owned = [$has_settings];

root method .uninit_has_settings() {
    settings = 0;
    defined_settings = 0;
    local_settings = 0;
};

public method ._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 method .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 method .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, (this() + " already has a setting named ") + name);
    defined_settings = defined_settings.add(name, ._default_options());
    .configure_setting(name, options);
};

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

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

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

public method .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 method .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) == 'objnum)
            return (> (setting_data[fname]).get_setting(spec, definer, args) <);
        else
            return setting_data[fname];
    } else {
        return (definer.setting_data())[fname];
    }
};

public method .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) != 'objnum) && (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 method .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 method ._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 method ._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 method .configure_setting() {
    arg name, [options];
    var o, new;
    
    if (type(name) != 'string)
        throw(~type, "name must be a string.");
    if (options) {
        options = options[1];
        if (type(options) != 'dictionary)
            throw(~type, "options must be a dictionary.");
    } else {
        options = #[];
    }
    if (!dict_contains(defined_settings, name))
        throw(~namenf, (("Setting " + name) + " is not defined on ") + this());
    if (!options)
        return defined_settings[name];
    
    // 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 method .defined_settings() {
    return (| defined_settings.keys() |) || [];
};

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

public method .is_any() {
    arg definer, value, [args];
    
    return value;
};

public method .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 method .get_local_setting(): nooverride {
    arg name, definer, [args];
    
    return (| (settings[definer])[name] |) || throw(~setting, ("Setting \"" + name) + "\" has not been defined.");
};

root method .init_has_settings() {
    defined_settings = #[];
    local_settings = #[];
    settings = #[];
};

public method .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 method .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 method .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 'objnum:
            return $object_lib.to_dbref(value);
        default:
            return value;
    }
};

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

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

public method .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) != 'objnum) && (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, #[['objnum, ['namef, ['ref]]]]));
    
    // $#Edited: 13 Jun 96 01:13 $levi
};

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

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

public method .get_local_setting_boolean() {
    arg name, definer, [args];
    
    return (| (settings[definer])[name] |) || 0;
    
    // $#Edited: 27 Jan 96 16:21 Lynx ($lynx)
};

public method .get_defined_settings() {
    return defined_settings;
    
    // $#Edited: 03 Jul 96 13:30 $lynx
};


new object $editable: $foundation;

var $root manager = $editable;
var $root flags = ['variables, 'methods, 'code, 'core, 'fertile];
var $root created_on = 840422259;
var $root owners = [$editable];
var $editable types = 0;
var $root inited = 1;
var $root managed = [$editable];
var $root owned = [$editable];

public method .get_edit_types(): nooverride {
    return types;
    
    // $#Edited: 18 Aug 96 21:02 $jenner
};

public method .set_edit_types(): nooverride {
    arg t;
    
    (> .perms(sender()) <);
    types = t;
    
    // $#Edited: 18 Aug 96 21:02 $jenner
};

public method .all_edit_types(): nooverride {
    var i, l, t;
    
    l = [];
    for i in (.ancestors()) {
        if (type((| (t = i.get_edit_types()) |)) == 'list)
            l = l.union(i.get_edit_types());
    }
    return l;
    
    // $#Edited: 22 Aug 96 20:59 $jenner
};


new object $named: $has_settings, $editable;

var $root child_index = 10;
var $root fertile = 1;
var $root inited = 1;
var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'fertile, 'core, 'variables];
var $named name = ['normal, "named object", "a named object"];
var $named name_aliases = 0;
var $has_settings defined_settings = #[];
var $has_settings local_settings = #[];
var $has_settings settings = #[];
var $root manager = $named;
var $root managed = [$named];
var $root owners = [$named];
var $root owned = [$named];

public method .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 (new_name.match_regexp("^(a|an|the)\s+"))
        throw(~bad_name, "Do not include articles in name, use +u +n or +p instead.");
    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 method .name_aliases() {
    return name_aliases;
};

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

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

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

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

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

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

public method .namef() {
    arg type;
    
    switch (type) {
        case 'ref:
            return (((.name()) + " (") + this()) + ")";
        case 'xref:
            return ((this() + " (") + (.name())) + ")";
        case 'name:
            return .name();
        default:
            return (> pass(type) <);
    }
    
    // $# Edited 28 Oct 1995 21:06 Lynx ($lynx)
    // $#Edited: 15 Jul 96 22:18 $jenner
};

public method .hname() {
    arg [args];
    
    return ((("<a href=\"/bin/describe?" + this()) + "\">") + (.name())) + "</a>";
    
    // $#Edited: 13 Nov 95 20:31 Lynx ($lynx)
};

public method .match_name_exact() {
    arg str;
    
    return match_begin(name[2], str);
};

root method .uninit_named() {
    clear_var('name);
    clear_var('name_aliases);
};

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


new object $gender: $foundation, $named;

var $root inited = 1;
var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'core, 'variables];
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 = #[];
var $gender vpronouns = 0;
var $gender apronouns = 0;
var $root manager = $gender;
var $root managed = [$gender];
var $root owners = [$gender];
var $root owned = [$gender];

root method .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 method .pronoun() {
    arg pronoun;
    
    return (> pronouns[pronoun] <);
    
    // $#Edited: 13 Mar 96 17:36 Levi ($user_levi)
};

public method .gender() {
    return gender;
};

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

public method .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 method .pronouns() {
    return pronouns;
};

public method .gender_name() {
    arg [caps];
    
    caps = [@caps, 'null][1];
    switch (caps) {
        case 'caps:
            return cgender_name;
        default:
            return gender_name;
    }
    
    // 9-26-95/12:49 Lynx ($lynx), moved from $gender.name: conflicts with $named.name()
};

public method .context() {
    return context;
};

public method .cml_pronouns() {
    return cml_pronouns;
};

public method .vpronouns() {
    return vpronouns;
};

public method .apronouns() {
    return apronouns;
};


new object $gender_first_person: $gender;

var $root trusted = [];
var $root inited = 1;
var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'variables, 'core];
var $gender cgender_name = "";
var $gender gender_name = "";
var $gender pronouns = #[['pr, "yourself"], ['pp, "your"], ['po, "you"], ['ps, "you"], ['pq, "yours"], ['prc, "Yourself"], ['ppc, "Your"], ['poc, "You"], ['psc, "You"], ['pqc, "Yours"]];
var $gender number = 1;
var $gender context = ["yourself", "your", "you", "you", "yours", "Yourself", "Your", "You", "You", "Yours"];
var $named name = ['normal, "Gender, First Person", "a Gender, First Person"];
var $gender vpronouns = #[["vpr", "yourself"], ["vpp", "your"], ["vpo", "you"], ["vps", "you"], ["vpq", "yours"], ["vprc", "Yourself"], ["vppc", "Your"], ["vpoc", "You"], ["vpsc", "You"], ["vpqc", "Yours"]];
var $gender apronouns = #[["apr", "yourself"], ["app", "your"], ["apo", "you"], ["aps", "you"], ["apq", "yours"], ["aprc", "Yourself"], ["appc", "Your"], ["apoc", "You"], ["apsc", "You"], ["apqc", "Yours"]];
var $root manager = $gender_first_person;
var $root managed = [$gender_first_person];
var $root owners = [$gender_first_person];
var $root owned = [$gender_first_person];


new object $gender_female: $gender;

var $root inited = 1;
var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'variables, '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"];
var $gender vpronouns = #[["vpr", "herself"], ["vpp", "her"], ["vpo", "her"], ["vps", "she"], ["vpq", "hers"], ["vprc", "Herself"], ["vppc", "Her"], ["vpoc", "Her"], ["vpsc", "She"], ["vpqc", "Hers"]];
var $gender apronouns = #[["apr", "herself"], ["app", "her"], ["apo", "her"], ["aps", "she"], ["apq", "hers"], ["aprc", "Herself"], ["appc", "Her"], ["apoc", "Her"], ["apsc", "She"], ["apqc", "Hers"]];
var $root manager = $gender_female;
var $root managed = [$gender_female];
var $root owners = [$gender_female];
var $root owned = [$gender_female];


new object $gender_first_person_plural: $gender;

var $root trusted = [];
var $root inited = 1;
var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'variables, 'core];
var $gender cgender_name = "";
var $gender gender_name = "";
var $gender pronouns = #[['pr, "yourselves"], ['pp, "your"], ['po, "you"], ['ps, "you"], ['pq, "yours"], ['prc, "Yourselves"], ['ppc, "Your"], ['poc, "You"], ['psc, "You"], ['pqc, "Yours"]];
var $gender number = 2;
var $gender context = ["yourselves", "your", "you", "you", "yours", "Yourselves", "Your", "You", "You", "Yours"];
var $named name = ['normal, "Gender, First Person Plural", "a Gender, First Person Plural"];
var $gender vpronouns = #[["vpr", "yourselves"], ["vpp", "your"], ["vpo", "you"], ["vps", "you"], ["vpq", "yours"], ["vprc", "Yourselves"], ["vppc", "Your"], ["vpoc", "You"], ["vpsc", "You"], ["vpqc", "Yours"]];
var $gender apronouns = #[["apr", "yourselves"], ["app", "your"], ["apo", "you"], ["aps", "you"], ["apq", "yours"], ["aprc", "Yourselves"], ["appc", "Your"], ["apoc", "You"], ["apsc", "You"], ["apqc", "Yours"]];
var $root manager = $gender_first_person_plural;
var $root managed = [$gender_first_person_plural];
var $root owners = [$gender_first_person_plural];
var $root owned = [$gender_first_person_plural];


new object $gender_male: $gender;

var $root inited = 1;
var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'variables, '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"];
var $gender vpronouns = #[["vpr", "himself"], ["vpp", "his"], ["vpo", "him"], ["vps", "he"], ["vpq", "his"], ["vprc", "Himself"], ["vppc", "His"], ["vpoc", "Him"], ["vpsc", "He"], ["vpqc", "His"]];
var $gender apronouns = #[["apr", "himself"], ["app", "his"], ["apo", "him"], ["aps", "he"], ["apq", "his"], ["aprc", "Himself"], ["appc", "His"], ["apoc", "Him"], ["apsc", "He"], ["apqc", "His"]];
var $root manager = $gender_male;
var $root managed = [$gender_male];
var $root owners = [$gender_male];
var $root owned = [$gender_male];


new object $gender_plural: $gender;

var $root trusted = [];
var $root inited = 1;
var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'variables, '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"];
var $gender vpronouns = #[["vpr", "themselves"], ["vpp", "their"], ["vpo", "them"], ["vps", "they"], ["vpq", "theirs"], ["vprc", "Themselves"], ["vppc", "Their"], ["vpoc", "Them"], ["vpsc", "They"], ["vpqc", "Theirs"]];
var $gender apronouns = #[["apr", "themselves"], ["app", "their"], ["apo", "them"], ["aps", "they"], ["apq", "theirs"], ["aprc", "Themselves"], ["appc", "Their"], ["apoc", "Them"], ["apsc", "They"], ["apqc", "Theirs"]];
var $root manager = $gender_plural;
var $root managed = [$gender_plural];
var $root owners = [$gender_plural];
var $root owned = [$gender_plural];


new object $gender_neuter: $gender;

var $root inited = 1;
var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'variables, '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"];
var $gender vpronouns = #[["vpr", "itself"], ["vpp", "its"], ["vpo", "it"], ["vps", "it"], ["vpq", "its"], ["vprc", "Itself"], ["vppc", "Its"], ["vpoc", "It"], ["vpsc", "It"], ["vpqc", "Its"]];
var $gender apronouns = #[["apr", "itself"], ["app", "its"], ["apo", "it"], ["aps", "it"], ["apq", "its"], ["aprc", "Itself"], ["appc", "Its"], ["apoc", "It"], ["apsc", "It"], ["apqc", "Its"]];
var $root manager = $gender_neuter;
var $root managed = [$gender_neuter];
var $root owners = [$gender_neuter];
var $root owned = [$gender_neuter];


new object $has_commands: $foundation;

var $root fertile = 1;
var $root inited = 1;
var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'fertile, 'core, 'variables];
var $root child_index = 4;
var $has_commands shortcuts = #[];
var $has_commands local = 0;
var $has_commands remote = 0;
var $root manager = $has_commands;
var $root managed = [$has_commands];
var $root owners = [$has_commands];
var $root owned = [$has_commands];

public method .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).setadd_elem((cmd[1])[1], [@cmd[1], template, method, cmd[2]]));
};

public method .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 method .local_commands() {
    return local || #[];
};

public method .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 method .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 method .remote_commands() {
    return remote || #[];
};

root method .init_has_commands() {
    local = (remote = (shortcuts = #[]));
};

root method .uninit_has_commands() {
    .clear_variables(@.variables());
};

public method .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) <);
    shortcut = (relation[1])[1];
    relation = (relation[2])[2];
    if (!shortcuts)
        shortcuts = #[];
    shortcuts = shortcuts.add(shortcut, [method, relation]);
};

public method .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 method .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 method .get_shortcut_info() {
    arg shortcut;
    
    return (| shortcuts[shortcut] |) || throw(~shortcutnf, ("Shortcut \"" + shortcut) + "\" is not defined on this object.", shortcut);
};

public method .shortcuts() {
    return shortcuts || #[];
};

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

public method .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;
};


new object $described: $named, $has_commands;

var $described prose = [];
var $root inited = 1;
var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'variables, 'core];
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, []]]]]]]];
var $root manager = $described;
var $editable types = ["prose"];
var $root managed = [$described];
var $root owners = [$described];
var $root owned = [$described];

root method .init_described() {
    prose = [];
};

protected method .description() {
    arg flags;
    var out, name;
    
    // No option of getting the name, you always do
    out = (<$j_ctext_frob, [[(<$j_format, ["subj", [], [.name()], 'do_subj]>)], #[]]>);
    if (flags['prose])
        return [out, .prose()];
    return [out];
    
    // $# Edited 20 Oct 1995 16:08 Lynx ($lynx)
    // $#Edited: 18 Jul 96 20:14 $jenner
};

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

public method .set_prose() {
    arg new;
    
    (> .perms(sender()) <);
    switch (type(new)) {
        case 'string, 'list:
            new = (> $j_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 method .uninit_described() {
    prose = 0;
};

public method .look_at_cmd() {
    arg [args];
    
    return .get_description(#[['actor, sender()], ['exclude, this()]]);
};

public method .get_description(): nooverride {
    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);
};

public method .look_in_cmd() {
    arg cmdstr, cmd, type, this;
    
    return .get_description(#[['actor, sender()], ['type, type], ['exclude, this()]]);
};

public method .edit_prose() {
    var p;
    
    (> .perms(sender()) <);
    p = .prose();
    if (type(p) == 'frob)
        p = p.uncompile();
    (> sender().invoke_editor(this(), '_edit_prose_callback, p, []) <);
    
    // $#Edited: 18 Aug 96 21:02 $jenner
};

public method ._edit_prose_callback() {
    arg text, client_data;
    
    (> .perms(sender()) <);
    .set_prose(text);
    return "Description set.";
    
    // $#Edited: 18 Aug 96 20:14 $jenner
};


new object $gendered: $named;

var $root inited = 1;
var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'variables, 'core];
var $gendered gender = 0;
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, []]]]];
var $root manager = $gendered;
var $root managed = [$gendered];
var $root owners = [$gendered];
var $root owned = [$gendered];

public method .gender_context() {
    return gender.pronoun('po);
};

root method .init_gendered() {
    gender = $gender_neuter;
};

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

public method .gender() {
    arg [args];
    
    return gender;
};


new object $environment: $foundation;

var $root inited = 1;
var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'core, 'variables];
var $root manager = $environment;
var $root managed = [$environment];
var $root owners = [$environment];
var $root owned = [$environment];

public method .environment() {
    return [];
};

public method .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)).mmap('name)).to_english("", " or "))) + ".");
    }
    
    // $#Edited: 06 Aug 96 23:39 $levi
};

public method .local_to_environment() {
    arg obj;
    
    return obj in (.environment());
};


new object $physical: $event_handler, $described, $gendered, $environment, $has_settings;

var $root trusted = [];
var $root inited = 1;
var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'variables, 'core];
var $root owners = [$physical];
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 = #[];
var $root manager = $physical;
var $root managed = [$physical];
var $root owned = [$physical];

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

public method .visibility() {
    arg [args];
    
    return visibility;
};

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

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

public method .vr_examine() {
    // $#Edited: 12 Jun 96 22:44 $lynx
};


new object $has_messages: $foundation, $editable;

var $root fertile = 1;
var $root manager = $has_messages;
var $root owners = [$has_messages];
var $root inited = 1;
var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'fertile, 'core, 'variables];
var $has_messages messages = 0;
var $has_messages message_info = 0;
var $editable types = ["messages"];
var $root managed = [$has_messages];
var $root owned = [$has_messages];

public method .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 method .message_info() {
    arg name, field;
    var m;
    
    catch ~keynf
        m = message_info[name];
    with
        throw(~messagenf, ("Message " + name) + " is not defined here.");
    switch (field) {
        case 'evaluator:
            return (| m['evaluator] |) || $j_bs_eval;
        case 'compiler:
            return (| m['compiler] |) || $j_compiler;
        case 'uncompiler:
            return (| m['uncompiler] |) || $j_uncompiler;
        default:
            throw(~fieldnf, "Invalid field.");
    }
    
    // $#Edited: 03 Mar 96 15:31 Lynx ($lynx)
};

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

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

public method .evalutor() {
    arg name;
    
    return (defined_messages[name])[1];
};

public method .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 method .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 method .del_message() {
    arg name;
    var mess, kids;
    
    (> .perms(sender(), 'writer) <);
    message_info = message_info.del(name);
};

public method .local_messages() {
    return messages || #[];
};

public method .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 method .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 method .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 method .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 method .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 method .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 method .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 method ._del_message() {
    arg name;
    var mess;
    
    mess = messages[sender()];
    mess = mess.del(name);
    messages = messages.add(sender(), mess);
};

public method ._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 method .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 method .uninit_has_messages() {
    messages = 0;
    defined_messages = 0;
};

public method ._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 method .unset_message() {
    arg message, definer;
    var mes;
    
    mes = (| messages[definer] |) || #[];
    mes = mes.del(message);
    messages = messages.add(definer, mes);
};

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

public method .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]>);
};

public method .edit_messages() {
    var msg, s;
    
    (> .perms((s = sender())) <);
    msg = (s.display_messages("", this())).flatten();
    (> s.invoke_editor(this(), '_edit_messages_callback, msg, []) <);
};

public method ._edit_messages_callback() {
    arg text, client_data;
    var errors, i, m, def;
    
    (> .perms(sender()) <);
    for i in (text) {
        if (i.trim()) {
            if ((m = i.match_pattern("$* (*):"))) {
                sender().tell("Compiling messages defined for $%s (%s).".format(m[1], m[2]));
                def = $object_lib.to_dbref(m[1]);
            } else if ((m = i.match_pattern("$*:"))) {
                sender().tell("Compiling messages defined for $%s.".format(m[1]));
                def = $object_lib.to_dbref(m[1]);
            } else if ((m = i.match_pattern("*=*"))) {
                sender().set_message((m[1]).trim(), (m[2]).trim(), def);
            } else {
                sender().tell("Could not parse `%s`.".format(i));
            }
        }
    }
};


new object $exit: $physical, $has_messages;

var $exit source = $void;
var $exit dest = $void;
var $exit lock = <$true_lock_frob, []>;
var $exit coordinates = 0;
var $root child_index = 267;
var $root fertile = 1;
var $root inited = 1;
var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'fertile, 'variables, 'core];
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", <$j_ctext_frob, [["You take ", <$j_generator, ["exit", [], [], 'gen_exit]>, "."], #[['this, #4]]]>], ["exit.source", <$j_ctext_frob, [[<$j_generator, ["actor", [], [], 'gen_actor]>, " goes through ", <$j_generator, ["exit", [], [], 'gen_exit]>, "."], #[['this, #4]]]>], ["exit.dest", <$j_ctext_frob, [[<$j_generator, ["actor", [], [], 'gen_actor]>, " arrives."], #[['this, #4]]]>]]]];
var $has_messages message_info = #[["exit", #[]]];
var $root manager = $exit;
var $root managed = [$exit];
var $root owners = [$exit];
var $root owned = [$exit];

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

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

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

public method .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 themanager (") + ((.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());
    
    //// use hooks
    //   $movement_event.new(a, .source(), .dest()).dispatch();
};

public method .lock() {
    return lock;
};

public method .attach(): nooverride {
    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 method .dest() {
    return dest;
};

public method .source() {
    return source;
};

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

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

public method .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 method .lock_cmd() {
    arg cmdstr, cmd, this;
    
    if (!(| .perms(sender()) |))
        return ((("Only " + ((.manager()).name())) + " can lock ") + (.name())) + "!";
    lock = $false_lock_frob.new();
    return "You lock " + (.name());
};

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

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

public method .is_visible_to() {
    arg whom;
    
    return (.visibility()) >= ((whom.location()).darkness());
    
    // $# Edited 21 Oct 1995 18:06 Lynx ($lynx)
};


new object $command_cache: $has_commands;

var $root child_index = 3;
var $root fertile = 1;
var $root manager = $command_cache;
var $root created_on = 796605573;
var $root inited = 1;
var $root flags = ['methods, 'code, 'fertile, 'variables, 'core];
var $command_cache shortcut_cache = 0;
var $command_cache remote_cache = 0;
var $command_cache local_cache = 0;
var $has_commands shortcuts = #[];
var $root managed = [$command_cache];
var $root owners = [$command_cache];
var $root owned = [$command_cache];

root method .uninit_command_cache() {
    (| .purge_caches() |);
    
    // $#Edited: 06 Jan 96 13:18 Lynx ($lynx)
};

public method .rehash_caches() {
    var cmd, obj, part, element;
    
    (> .perms(sender()) <);
    (| .purge_caches() |);
    if (!(.is_command_cache())) {
        // if we are not an official 'cache', just cache commands defined on us
        for cmd in (.local_commands()) {
            for part in (cmd[2])
                .add_to_local_cache(part[1]);
        }
        shortcut_cache = (.shortcuts()).to_list();
    } else {
        // otherwise cache all defined commands
        (> .add_object_to_local_cache(this()) <);
        shortcut_cache = .all_shortcuts();
    }
    
    // remote caches are different, and HAVE to be specific to the user
    if (.is($location)) {
        for obj in ([this()] + (.contents()))
            (> .add_object_to_remote_cache(obj) <);
    }
    
    // $#Edited: 13 Jan 96 13:17 Lynx ($lynx)
};

public method .add_object_to_remote_cache() {
    arg obj;
    var info, thing, element, part;
    
    info = (| obj.all_remote_commands() |);
    if (info) {
        for element in (info) {
            for part in (element[2])
                .add_to_remote_cache(part[1], element[1]);
        }
    }
};

public method .purge_caches() {
    (> .perms(sender()) <);
    (| clear_var('shortcut_cache) |);
    (| clear_var('remote_cache) |);
    (| clear_var('local_cache) |);
};

private method .add_object_to_local_cache() {
    arg obj;
    var info, thing, element, part;
    
    info = (| obj.all_local_commands() |);
    if (info) {
        for element in (info) {
            for part in (element[2])
                .add_to_local_cache(part[1]);
        }
    }
    
    // $#Edited: 06 Jan 96 13:18 Lynx ($lynx)
};

public method .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]]);
        }
    }
    
    // $#Edited: 06 Jan 96 13:19 Lynx ($lynx)
};

public method .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.setadd_elem(part, cmd);
};

public method .match_in_shortcut_cache() {
    arg str, cmd, args;
    var shortcut, match, obj, shorts;
    
    for obj in ([this()] + parents()) {
        if ((shorts = obj.shortcut_cache())) {
            for shortcut in (shorts) {
                if ((match = str.match_pattern(shortcut[1])))
                    return ['shortcut, [(shortcut[2])[1], [str, @$command_lib.handle_shortcut_fields((shortcut[2])[2], match)]]];
            }
        }
    }
    return 0;
    
    // $#Edited: 08 Jan 96 18:56 Lynx ($lynx)
};

public method .shortcut_cache() {
    return shortcut_cache;
};

public method .remote_cache() {
    return remote_cache;
};

public method .local_cache() {
    return local_cache;
};

public method .match_in_remote_cache() {
    arg str, cmd, args;
    var cache, definer, command, info, cdef, match, matched, templates;
    
    cache = (| .find_in_remote_caches(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 = args.match_template(cdef[2]);
                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];
    
    // $#Edited: 08 Jan 96 18:53 Lynx ($lynx)
};

public method .match_in_local_cache() {
    arg str, cmd, args;
    var command, match, matched, templates, info, cdef, def;
    
    templates = (matched = []);
    for command in (.find_in_local_caches(cmd)) {
        info = .get_command_info('local, command);
        if (!info)
            continue;
        for cdef in (info) {
            match = match_template(args, cdef[2]);
            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 method .find_in_local_cache() {
    arg cmd;
    
    return (> local_cache[cmd] <);
    
    // $#Edited: 08 Jan 96 18:48 Lynx ($lynx)
};

public method .cache_init() {
    if (!(sender().has_ancestor(this())))
        throw(~nochild, ((sender() + " is not a descendant of ") + this) + ".\n");
    if (.is_command_cache()) {
        if ((!local_cache) || (!remote_cache))
            .rehash_caches();
    }
    
    // $#Edited: 08 Jan 96 18:32 Lynx ($lynx)
};

public method .is_command_cache() {
    return 'command_cache in (.flags());
    
    // $#Edited: 06 Jan 96 13:07 Lynx ($lynx)
};

public method .set_as_command_cache() {
    arg makecache;
    
    (> .perms(sender(), 'manager) <);
    if (makecache)
        (> .add_flag('command_cache) <);
    else
        (> .del_flag('command_cache) <);
    
    // $#Edited: 06 Jan 96 13:07 Lynx ($lynx)
};

protected method .find_in_command_cache() {
    arg cmd_word, cache;
    var matches, match, obj, objs;
    
    matches = #[];
    for obj in ([this()] + parents()) {
        match = (| obj.(cache)()[cmd_word] |);
        if (match)
            matches = matches.union(match);
    }
    return matches;
    
    // $#Edited: 08 Jan 96 18:47 Lynx ($lynx)
};

public method .find_in_remote_cache() {
    arg cmd;
    
    return (> remote_cache[cmd] <);
    
    // $#Edited: 08 Jan 96 18:48 Lynx ($lynx)
};

protected method .find_in_local_caches() {
    arg cmd_word;
    var matches, match, obj, objs;
    
    matches = [];
    for obj in ([this()] + parents()) {
        if ((match = (| obj.find_in_local_cache(cmd_word) |)))
            matches = matches.union(match);
    }
    return matches;
    
    // $#Edited: 08 Jan 96 18:51 Lynx ($lynx)
};

public method .find_in_remote_caches() {
    arg cmd_word;
    var matches, match, obj, objs;
    
    matches = [];
    for obj in ([this()] + parents()) {
        if ((match = (| obj.find_in_remote_cache(cmd_word) |)))
            matches = matches.union(match);
    }
    return matches;
    
    // $#Edited: 08 Jan 96 18:51 Lynx ($lynx)
};

public method .cache_uninit() {
    var user;
    
    // if (!.is($user))
    //    throw(~notuser, this() + " is not a user object.");
    if (!(sender().has_ancestor(this())))
        throw(~nochild, ((sender() + " is not a descendant of ") + this) + ".\n");
    if (.is_command_cache()) {
        // see if anybody still needs us, otherwise purge the caches
        for user in ($user_db.connected()) {
            if (user.has_ancestor(this()))
                return;
        }
        .purge_caches();
    }
};


new object $user_interfaces: $command_cache;

var $root child_index = 12;
var $root fertile = 1;
var $root inited = 1;
var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'fertile, 'variables, 'core];
var $has_commands shortcuts = #[];
var $command_cache shortcut_cache = 0;
var $command_cache remote_cache = 0;
var $command_cache local_cache = 0;
var $root manager = $user_interfaces;
var $root managed = [$user_interfaces];
var $root owners = [$user_interfaces];
var $root owned = [$user_interfaces];


new object $interaction: $user_interfaces;

var $root inited = 1;
var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'variables, '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, []]]]]]], ["emote", [["emote", "*", "emote <any>", 'emote_cmd, #[[1, ['any, []]]]]]], ["@channel", [["@channel", "*", "@channel <any>", 'channel_cmd, #[[1, ['any, []]]]]]], ["@listen?-to-channel|@listens", [["@listen?-to-channel|@listens", "*", "@listen?-to-channel|@listens <any>", 'channel_listen_cmd, #[[1, ['any, []]]]]]], ["@page", [["@page", "* with *", "@page <any> with <any>", 'remote_cmd, #[[1, ['any, []]], [3, ['any, []]]]]]], ["@paste?-to", [["@paste?-to", "*", "@paste?-to <any>", 'paste_cmd, #[[1, ['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]]], ["\"*", ['say_cmd, ["say", 1]]], [":*", ['emote_cmd, ["emote", 1]]], ["#*", ['channel_cmd, ["#", 1]]], ["--*", ['remote_cmd, ["@page", "", "with", 1]]], ["-* *", ['remote_cmd, ["@page", 1, "with", 2]]]];
var $command_cache shortcut_cache = 0;
var $command_cache remote_cache = 0;
var $command_cache local_cache = 0;
var $root manager = $interaction;
var $root managed = [$interaction];
var $root owners = [$interaction];
var $root owned = [$interaction];

protected method .channel_cmd() {
    arg cmdstr, cmd, args;
    var channel, what;
    
    args = args.explode_quoted();
    if (!args) {
        if (cmd == "#")
            return "Syntax => `#<channel> <text>`";
        return ("Syntax => `" + cmd) + " <channel> <text>`";
    }
    channel = (args[1]).to_symbol();
    what = (args.subrange(2)).join();
    $channels.announce(channel, what, 'name);
    
    // $# Edited 11 Nov 1995 17:20 Lynx ($lynx)
    // $#Edited: 18 Jul 96 14:56 $levi
};

protected method .say_to_cmd() {
    arg cmdstr, com, who, prep, message;
    var targets, target, line;
    
    targets = (> ._parse_interaction_reference(who, "say") <);
    if (targets) {
        ._add_interaction('objs, targets);
        line = (((.name()) + " (to ") + (targets.map_to_english('name))) + ") ";
        if (message)
            line = line + (((message[1]) == ":") ? message.subrange(2) : (((($code_lib.punctuation_type(message)) + "s, \"") + message) + "\""));
        else
            line = line + "says nothing.";
        (.location()).announce(line);
    }
    
    // $# Edited 01 Nov 1995 14:22 Lynx ($lynx)
    // $#Edited: 11 Dec 95 16:36 Jenner ($jenner)
};

protected method .whisper_cmd() {
    arg cmdstr, com, what, prep, who;
    var loc;
    
    who = .match_env_nice(who);
    loc = .location();
    if ((who.location()) != loc)
        return "You must be in the same room as a person, to whisper to them.";
    who.tell((((.name()) + " whispers, \"") + what) + "\"");
    .tell(((("You whisper, \"" + what) + "\" to ") + (who.name())) + ".");
    loc.announce((((.name()) + " whispers to ") + (who.name())) + ".", who, this());
    
    // $# Edited 05 Nov 1995 14:04 Lynx ($lynx)
};

protected method ._interact() {
    arg cmdstr, cmd, what;
    var stage;
    
    stage = $interaction.get_stage();
    
    // cwrite .name()+stage[cmd]+what to the room
    if (cmd in (stage.keys()))
        (.location()).announce(((.name()) + (stage[cmd])) + what);
    else
        throw(~unknowncmd, ("Unknown cmd '" + cmd) + "'");
    
    // $# Edited 05 Nov 1995 14:05 Lynx ($lynx)
};

public method .get_stage() {
    return stage;
    
    // $#Edited: 12 Oct 95 21:02 Dancer ($dancer)
};

protected method .think_cmd() {
    arg cmdstr, cmd, what;
    
    (.location()).announce((((.name()) + " . o O ( ") + what) + " )");
    
    // $# Edited 01 Nov 1995 14:37 Lynx ($lynx)
};

protected method .emote_cmd() {
    arg cmdstr, com, what;
    
    if (what && ((what[1]) == ":"))
        (.location()).announce((.name()) + (what.subrange(2)));
    else
        (.location()).announce(((.name()) + " ") + what);
    
    // $#Edited: 12 Oct 95 21:02 Dancer ($dancer)
};

protected method .say_cmd() {
    arg cmdstr, cmd, what;
    var type, how, idx;
    
    if (what)
        how = $code_lib.punctuation_type(what);
    else
        how = "say";
    (.location()).announce((((((.name()) + " ") + how) + "s, \"") + what) + "\"");
    
    // $#Edited: 12 Oct 95 21:52 Dancer ($dancer)
    // $#Edited: 12 Oct 95 21:02 Dancer ($dancer)
};

protected method .paste_cmd() {
    arg cmdstr, com, who;
    var obj, text;
    
    if (who) {
        who = who.explode();
        if (((who.length()) > 1) && ((who[1]) == "to"))
            who = who.delete(1);
        who = (> .match_env_nice(who.join()) <);
    }
    text = .read();
    if (text == 'aborted)
        return .tell("@paste aborted.");
    else if (!text)
        return .tell("@paste nothing?");
    text = [$string.center((" " + (.name())) + " (@paste's) ", 79, "-"), @text, $string.center(" + Finis + ", 79, "-")];
    if (who) {
        (| who.tell(text) |);
        .tell(((((text.length()) - 2) + " lines of text pasted to ") + (who.name())) + ".");
    } else {
        (.location()).announce(text);
        .tell(((text.length()) - 2) + " lines of text pasted");
    }
    
    // $#Edited: 18 Jul 96 14:56 $levi
};

protected method .esay_cmd() {
    arg cmdstr, cmd, how, prep, what;
    
    (.location()).announce((((((.name()) + " ") + how) + ", \"") + what) + "\"");
    
    // $#Edited: 12 Oct 95 21:02 Dancer ($dancer)
};

protected method .right_encapsulate_cmd() {
    arg cmdstr, right, line;
    var space, line, left, right;
    
    space = " ";
    if (!line)
        space = "";
    switch (right) {
        case "]":
            left = "[";
        case ")":
            left = "(";
    }
    (.location()).announce((((left + (.name())) + space) + line) + right);
    
    // $# Edited 05 Nov 1995 14:06 Lynx ($lynx)
};

protected method ._parse_interaction_reference() {
    arg targets, what, [syn];
    var recip, target;
    
    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;
    
    // $#Edited: 12 Oct 95 21:02 Dancer ($dancer)
};

protected method ._get_interaction() {
    arg key;
    
    return (| interaction[key] |) || 0;
    
    // $#Edited: 12 Oct 95 21:02 Dancer ($dancer)
};

protected method ._add_interaction() {
    arg key, value;
    
    if (!interaction)
        interaction = #[];
    if (type(value) != 'list)
        value = [value];
    value = [time(), value];
    interaction = interaction.add(key, value);
    
    // $#Edited: 12 Oct 95 21:02 Dancer ($dancer)
};

public method ._purge_interaction() {
    interaction = 0;
    
    // $#Edited: 12 Oct 95 21:02 Dancer ($dancer)
};

protected method .spoof_cmd() {
    arg cmdstr, cmd, what;
    var name;
    
    name = .name();
    if (!(((name + " ") in what) || ((" " + name) in what)))
        what = (what + "     -- ") + name;
    (.location()).announce(what);
    
    // $# Edited 05 Nov 1995 14:06 Lynx ($lynx)
};

protected method .channel_listen_cmd() {
    arg cmdstr, cmd, args;
    var c;
    
    if (args) {
        if (args && ((args[1]) == "#"))
            args = args.subrange(2);
        if (!args) {
            .tell("Invalid channel name.");
        } else {
            c = ((args.explode())[1]).to_symbol();
            .add_channel(c);
            .tell(("Added channel #" + tostr(c)) + ".");
        }
    }
    .tell("Currently listening to the following channels:");
    for c in (.channels())
        .tell(("    #" + tostr(c[1])) + ((type(c[2]) == 'list) ? (c[2]).mmap('name) : ""));
    
    // $# Edited 16 Oct 1995 22:12 Lynx ($lynx)
};

protected method .remote_cmd() {
    arg cmdstr, com, who, prep, str;
    var target, line, fstr, wstr, type;
    
    if (str && ((str[1]) == ":")) {
        type = 'emote;
        str = str.subrange(2);
    } else {
        type = 'say;
    }
    who = (> ._parse_interaction_reference(who, tostr(type)) <);
    if (who) {
        ._add_interaction('objs, who);
        line = .name();
        if (str && ((str[1]) == ":"))
            str = str.subrange(2);
        else
            line = line + " ";
        if (type == 'emote) {
            line = line + str;
        } else {
            if (str)
                line = line + ($code_lib.punctuation_type(str));
            else
                line = line + "say";
            line = ((line + "s, \"") + str) + "\"";
        }
        wstr = (who.mmap('name)).to_english();
        fstr = "[from " + ((.location()).name());
        if ((who.length()) > 1)
            fstr = (fstr + ", to ") + wstr;
        fstr = (fstr + "] ") + line;
        for target in (who) {
            if (!(target.connected()))
                .tell((target.name()) + " is not connected.");
            (| target.tell(fstr) |);
        }
        .tell((("[to " + ((who.mmap('name)).to_english())) + "] ") + line);
    }
    
    // $# Edited 22 Oct 1995 21:10 Lynx ($lynx)
};


new object $located: $physical;

var $located inited = 0;
var $located location = $lost_and_found;
var $located obvious = 1;
var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'variables, 'core];
var $gendered gender = $gender_neuter;
var $described prose = [];
var $named name = ['uniq, "Generic Located Object", "the Generic Located Object"];
var $named name_aliases = [];
var $root manager = $located;
var $root managed = [$located];
var $root owners = [$located];
var $root owned = [$located];

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

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

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

public method .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 method .location() {
    return location || $void;
};

public method .will_move() {
    arg mover, place;
    
};

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

public method .realm() {
    return realm;
};

root method .uninit_located() {
    (.location()).del_sender_from_contents();
};

public method .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 method .match_environment_all() {
    arg s;
    
    if (s == "here")
        return [location, @(> pass(@args) <)];
    else
        return (> pass(s) <);
};

public method .obvious() {
    return obvious;
};

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

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

public method .announce() {
    arg [args];
    
};

public method .is_obvious_to() {
    arg whom;
    
    // will later do something creative here
    return 1;
};


new object $public: $foundation;

var $root fertile = 1;
var $root inited = 1;
var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'fertile, 'core, 'variables];
var $public public = [];
var $root manager = $public;
var $root managed = [$public];
var $root owners = [$public];
var $root owned = [$public];

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

public method .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 method .public() {
    return public;
};


new object $location: $physical, $command_cache;

var $location contents = [];
var $root fertile = 1;
var $root inited = 1;
var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'fertile, 'variables, 'core];
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 = #[];
var $root manager = $location;
var $root managed = [$location];
var $root owners = [$location];
var $root owned = [$location];

root method .init_location() {
    contents = [];
};

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

public method .contents() {
    return contents || [];
};

public method .contains() {
    arg obj;
    
    return (obj in (.contents())) ? 1 : 0;
};

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

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

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

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

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

public method .add_sender_to_contents(): nooverride {
    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());
    
    // $#Edited: 19 Mar 96 02:56 Levi ($user_levi)
};

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

public method .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 method .environment() {
    return [this()] + contents;
};

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

public method .contents_accept_mail() {
    return 1;
};

public method .realm() {
    arg [args];
    var loc;
    
    loc = "";
    if ((| .location() |))
        loc = (.location()).realm();
    return ((loc + "[") + (.name())) + "]";
    
    // $# Edited 05 Nov 1995 14:03 Lynx ($lynx)
};

public method .realm_name() {
    return "";
};

public method .add_frob_to_contents() {
    arg frob;
    
    if (caller() != $thing_frob)
        throw(~perm, "Caller is not $thing_frob.");
    if (type(frob) != 'frob)
        throw(~type, "Argument is not a frob.");
    if ((frob.location()) != this())
        throw(~location, "Sorry, but you're not here.");
    contents = (.contents()).setadd(frob);
};

public method .del_frob_from_contents() {
    arg frob;
    
    if (caller() != $thing_frob)
        throw(~perm, "Caller is not $thing_frob.");
    if (type(frob) != 'frob)
        throw(~type, "Argument not a frob.");
    contents = contents.setremove(frob);
};


new object $place: $location, $public, $has_messages;

var $root child_index = 161;
var $root fertile = 1;
var $root inited = 1;
var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'fertile, 'variables, 'core];
var $has_messages messages = #[[$place, #[["housekeeper", <$j_ctext_frob, [["The housekeeper hauls ", <$j_generator, ["actor", [], [], 'gen_actor]>, " away."], #[['this, #4]]]>]]]];
var $has_messages message_info = #[["housekeeper", #[]]];
var $gendered gender = $gender_neuter;
var $described prose = [];
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 = #[["l", [["l?ook"], [$described]]], ["lo", [["l?ook"], [$described]]], ["loo", [["l?ook"], [$described]]], ["look", [["l?ook"], [$described]]]];
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, []]]]];
var $root manager = $place;
var $has_commands local = #[];
var $root managed = [$place];
var $root owners = [$place];
var $root owned = [$place];

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

root method .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 method .environment() {
    return pass() + exits;
};

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

public method .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 method .add_exit(): nooverride {
    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 method ._add_exit(): nooverride {
    arg obj;
    
    // use .add_exit()
    .perms(sender(), 'this);
    exits = [@exits, obj];
};

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

public method .del_exit(): nooverride {
    var exit;
    
    exit = sender();
    .perms(caller(), $exit);
    (| ._del_exit(exit) |);
    (| ._del_coordinate(exit) |);
    (| ._del_coordinate(exit.dest()) |);
};

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

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

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

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

public method .realm() {
    arg [args];
    var tmp, r;
    
    return realm;
};

public method .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 method .did_housekeep() {
    arg who;
    var m, v;
    
    (> .perms(sender(), $housekeeper) <);
    .announce(.eval_message("housekeeper", #[["$actor", who], ["actor", who.name()], ["$here", this()], ["here", .name()]], $place));
    
    // $# Edited 05 Nov 1995 13:59 Lynx ($lynx)
};

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

public method .set_realm() {
    arg value;
    
    (> .perms(sender()) <);
    realm = value;
    
    // $#Edited: 22 Aug 96 21:29 $jenner
};

public method .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 method ._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 != actor) && (| 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 method .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 method .visible_exits() {
    var obv, exit;
    
    obv = [];
    for exit in (.exits()) {
        if ((exit.visibility()) >= (.visibility()))
            obv = [@obv, exit];
    }
    return obv;
};

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

public method .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 method .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 method .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.mmap(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.join()] : [];
    }
    
    // $#Edited: 18 Jul 96 14:56 $levi
};

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

public method .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 method .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 method .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 method .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 method .set_objname(): nooverride {
    arg new_objname;
    
    if ((caller() != $place) && (!(sender() in ($sys.system()))))
        throw(~perm, "Place objname can only be changed by $place.");
    (> pass(new_objname) <);
};

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

public method .did_attach() {
    arg type, [by_whom];
    
    if (caller() != $exit)
        throw(~perm, "Caller is not $exit.");
    .add_object_to_remote_cache(sender());
    
    // $#Edited: 06 Jan 96 14:34 Lynx ($lynx)
};

public method .rehash_caches() {
    var exit;
    
    (> pass() <);
    for exit in (exits)
        (> .add_object_to_remote_cache(exit) <);
    
    // $#Edited: 06 Jan 96 14:34 Lynx ($lynx)
};

public method .coordinates() {
    return coordinates;
};

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

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

public method .darkness() {
    arg [args];
    
    return darkness;
};

public method .display_setting_realm() {
    arg frob;
    
    return frob.name();
    
    // $# Edited 16 Oct 1995 18:01 Lynx ($lynx)
};


new object $nowhere: $place;

var $root inited = 1;
var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'variables, 'core];
var $location contents = [$located_location, $body, $user, $reaper_log, $log, $http_log, $note, $slate, $login_log, $generic_map, $map_of_taobh_thiar, $on_location, $in_location];
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;
var $place coordinates = #[];
var $public public = [];
var $command_cache shortcut_cache = 0;
var $command_cache remote_cache = #[["@lock", [["@lock"], [$thing]]], ["@unlock", [["@unlock"], [$thing]]], ["@boot", [["@boot"], [$thing]]], ["l", [["l?ook"], [$described, $located_location]]], ["lo", [["l?ook"], [$described, $located_location]]], ["loo", [["l?ook"], [$described, $located_location]]], ["look", [["l?ook"], [$described, $located_location]]], ["erase", [["erase"], [$note]]], ["read", [["read|nread"], [$note]]], ["nread", [["read|nread"], [$note]]], ["write", [["write"], [$note]]], ["copy", [["copy"], [$note]]], ["light", [["light"], [#349]]], ["smoke", [["smoke"], [#346]]], ["wear", [["wear"], [$wearable_frob]]], ["remove", [["remove"], [$wearable_frob]]], ["foo", [["foo"], [#1002]]], ["boom", [["boom"], [#444]]], ["hit", [["hit"], [#444]]], ["shake", [["shake"], [#999]]], ["raise", [["raise"], [#999]]]];
var $command_cache local_cache = 0;
var $root manager = $nowhere;
var $root managed = [$nowhere];
var $root owners = [$nowhere];
var $root owned = [$nowhere];


new object $body_cave: $place;

var $root manager = $body_cave;
var $root created_on = 808865147;
var $root inited = 1;
var $root flags = ['methods, 'code, 'variables, '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 = #[["@lock", [["@lock"], [$thing]]], ["@unlock", [["@unlock"], [$thing]]], ["@boot", [["@boot"], [$thing]]], ["l", [["l?ook"], [$described, $located_location]]], ["lo", [["l?ook"], [$described, $located_location]]], ["loo", [["l?ook"], [$described, $located_location]]], ["look", [["l?ook", "look"], [$described, $located_location]]]];
var $command_cache local_cache = 0;
var $named name = ['uniq, "Body Cave", "the Body Cave"];
var $named name_aliases = [];
var $gendered gender = $gender_neuter;
var $described prose = [];
var $location contents = [$reaper, $builder, $guest, $no_one, $programmer, $admin, $player, $storyteller, #1001];
var $place exits = [];
var $place realm = $realm_of_creation;
var $place coordinates = #[];
var $physical visibility = -100;
var $root managed = [$body_cave];
var $root owners = [$body_cave];
var $root owned = [$body_cave];

public method .announce() {
    arg [who_cares];
    
    // $#Edited: 18 Aug 96 22:06 $jenner
};


new object $void: $place;

var $root inited = 1;
var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'variables, 'core];
var $location contents = [$thing, $lost_and_found, $thing_frob, $trash];
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;
var $place coordinates = #[];
var $public public = [];
var $command_cache shortcut_cache = 0;
var $command_cache remote_cache = #[["@lock", [["@lock"], [$thing]]], ["@unlock", [["@unlock"], [$thing]]], ["@boot", [["@boot"], [$thing]]], ["l", [["l?ook"], [$described, $located_location]]], ["lo", [["l?ook"], [$described, $located_location]]], ["loo", [["l?ook"], [$described, $located_location]]], ["look", [["l?ook"], [$described, $located_location]]]];
var $command_cache local_cache = 0;
var $root manager = $void;
var $root managed = [$void];
var $root owners = [$void];
var $root owned = [$void];


new object $detailed_place: $place;

var $root manager = $detailed_place;
var $root created_on = 809582204;
var $root inited = 1;
var $root flags = ['methods, 'code, 'variables, '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 $described prose = [];
var $location contents = [];
var $place exits = [];
var $place realm = $realm_of_creation;
var $place coordinates = #[];
var $root managed = [$detailed_place];
var $root owners = [$detailed_place];
var $root owned = [$detailed_place];


new object $the_pit: $place;

var $has_messages messages = #[[$place, #[["housekeeper", <$j_ctext_frob, [["Dust bunnies rise from under the furniture to envelope ", <$j_generator, ["actor", [], [], 'gen_actor]>, "'s comatose form, leaving behind a small pile of musty lint."], #[['this, #4]]]>]]]];
var $root inited = 1;
var $root manager = $the_pit;
var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'variables, 'core];
var $location contents = [];
var $gendered gender = $gender_neuter;
var $described prose = <$j_ctext_frob, [["This is a room with a low ceiling, worn couches and furniture, and walls worn from scribbling, burn marks, and other unrecognisable usages. Despite its cramped feel, there is a warm comfortable atmosphere, almost as if somebody could come here and just relax for a while. To the north is a cement opening in the wall."], #[['this, #4]]]>;
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 remote_cache = #[["l", [["l?ook"], [$described, $located_location]]], ["lo", [["l?ook"], [$described, $located_location]]], ["loo", [["l?ook"], [$described, $located_location]]], ["look", [["l?ook"], [$described, $located_location]]], ["step", [["step"], [#453]]], ["beat", [["beat"], [#453]]], ["jump", [["jump"], [#453]]], ["pet", [["pet"], [#453]]], ["@lock", [["@lock"], [$thing, $exit]]], ["@unlock", [["@unlock"], [$thing, $exit]]], ["@boot", [["@boot"], [$thing]]], ["erase", [["erase"], [$note]]], ["read", [["read|nread"], [$note]]], ["nread", [["read|nread"], [$note]]], ["write", [["write"], [$note]]], ["copy", [["copy"], [$note]]], ["bash", [["bash"], [#1493]]], ["bounce", [["bounce"], [#459]]]];
var $command_cache shortcut_cache = [];
var $root managed = [$the_pit];
var $root owners = [$the_pit];
var $root owned = [$the_pit];

public method .coreify_the_pit() {
    (> .perms(caller(), $sys) <);
    .set_realm($realm_of_creation);
    
    // $#Edited: 22 Sep 96 09:49 $user_brandon
};


new object $thing: $located, $public;

var $root child_index = 122;
var $root fertile = 1;
var $root inited = 1;
var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'fertile, 'variables, 'core];
var $located location = $void;
var $located obvious = 1;
var $gendered gender = $gender_neuter;
var $described prose = [];
var $named name = ['normal, "thing", "a 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 = <$true_lock_frob, []>;
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 = #[];
var $root manager = $thing;
var $root managed = [$thing];
var $root owners = [$thing];
var $root owned = [$thing];

public method .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 " + (.name())) + ".");
        loc.announce((((((sender().name()) + " boots ") + (.name())) + " from ") + ((.location()).name())) + ".", this(), sender());
        if ((sender().location()) != loc)
            (sender().location()).announce((((((sender().name()) + " boots ") + (.name())) + " from ") + ((.location()).name())) + ".", this(), sender());
        (> .move_to(dest) <);
    } with {
        return (traceback()[1])[2];
    }
    
    // $# Edited 05 Nov 1995 14:10 Lynx ($lynx)
};

public method .lock() {
    return lock;
};

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

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

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

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

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

public method .check_home() {
    arg definer, value, [args];
    var home;
    
    home = (> .match_environment(value) <);
    if ((!(| home.trusts(sender()) |)) && (!(| home.setting("public-home") |)))
        throw(~perm, ("You do not have permission to make " + (home.name())) + " your home.");
    return home;
    
    // $# Edited 16 Oct 1995 18:04 Lynx ($lynx)
};

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

public method .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.");
};


new object $located_location: $thing, $location;

var $root child_index = 6;
var $root fertile = 1;
var $root inited = 1;
var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'fertile, 'variables, 'core];
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;
var $has_commands remote = #[["l?ook", [["l?ook", "in|on *", "l?ook in|on <this>", 'look_in_cmd, #[[2, ['this, []]]]]]]];
var $root manager = $located_location;
var $root managed = [$located_location];
var $root owners = [$located_location];
var $root owned = [$located_location];

public method .environment() {
    return (.contents()) + pass();
};

public method .description() {
    arg flags;
    var desc, out, c;
    
    // Should actually change 'type' to be an object variable.
    if (!(flags.contains('type)))
        return (> pass(flags) <);
    out = [];
    for c in (.contents())
        out += [c.name()];
    return (> pass(flags) <) + [((((("There is " + (out.to_english())) + " ") + (flags['type])) + " ") + (.name())) + "."];
};


new object $body: $located_location;

var $root child_index = 18;
var $root fertile = 1;
var $root inited = 1;
var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'fertile, 'variables, 'core];
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;
var $root manager = $body;
var $root managed = [$body];
var $root owners = [$body];
var $root owned = [$body];

public method .tell() {
    arg [args];
    
};

public method .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 method .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 method .body_parts() {
    return body_parts;
};

public method .namef() {
    arg type;
    var str;
    
    switch (type) {
        case 'doing, 'nactivity, 'activity, 'titled:
            return .name();
        default:
            return (> pass(type) <);
    }
    
    // $# Edited 28 Oct 1995 21:08 Lynx ($lynx)
};

public method .available_body_parts() {
    return available_body_parts || ['head, 'rleg, 'lleg, 'rarm, 'larm, 'torso];
};

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

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

public method .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 method .set_following() {
    arg what;
    
    (> .perms(sender()) <);
    following = what;
};

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

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

public method .contents() {
    return (> pass() <) + (wearing || []);
    
    // $# Edited 19 Oct 1995 13:13 Lynx ($lynx)
};


new object $robot: $body;

var $root child_index = 2;
var $root inited = 1;
var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'variables, 'core];
var $named name = ['uniq, "Robot", "the Robot"];
var $named name_aliases = [];
var $gendered gender = $gender_neuter;
var $described prose = "A giant hungry rat. His red eyes glare at you malevontly.";
var $located location = $lost_and_found;
var $located obvious = 1;
var $robot tell_filters = [["* says, \"<this>, list commands\"", 'commands]];
var $body body_parts = #[];
var $location contents = [];
var $command_cache shortcut_cache = 0;
var $command_cache remote_cache = 0;
var $command_cache local_cache = 0;
var $root manager = $robot;
var $root managed = [$robot];
var $root owners = [$robot];
var $root owned = [$robot];

public method .tell() {
    arg text;
    var t, p;
    
    for t in (.tell_filters()) {
        p = t[1];
        p = p.replace("<this>", .name());
        if (text.match_pattern(p))
            $scheduler.add_task(1, t[2], text);
    }
    
    // $# Edited 05 Nov 1995 14:08 Lynx ($lynx)
};

public method .add_tell_filter() {
    arg mask, method;
    
    .perms(sender());
    if ((type(mask) != 'string) || (type(method) != 'symbol))
        throw(~type, "Args must be a string and a symbol.");
    tell_filters = [@tell_filters, [mask, method]];
};

public method .del_tell_filter() {
    arg mask;
    var t, tf;
    
    .perms(sender());
    if (type(mask) != 'string)
        throw("Argument must be a string");
    tf = [];
    for t in [1 .. tell_filters.length()] {
        if (((tell_filters[t])[1]) == mask) {
            if (t > 1)
                tf = [@tf, @tell_filters.subrange(1, t - 1)];
            if (t < (tell_filters.length()))
                tf = [@tf, @tell_filters.subrange(t + 1)];
            tell_filters = tf;
            return;
        }
    }
    throw(~tell_nf, "Mask not found.");
};

public method .tell_filters() {
    var t;
    
    catch ~methodnf
        t = pass();
    with
        t = [];
    return [@tell_filters, @t];
};

public method .good_morning() {
    arg text;
    
    text = $string.to_list(text, " ");
    .say("Good morning, " + (text[1]));
};

public method .say() {
    arg what;
    
    (.location()).announce((((.name()) + " says, \"") + what) + "\"");
    
    // $# Edited 05 Nov 1995 14:08 Lynx ($lynx)
};

public method .commands() {
    arg text;
    var o, who, t;
    
    //"* says, \"<this>, list commands\""
    text = $string.to_list(text, " ");
    who = $object_lib.to_dbref(text[1]);
    o = [(who.name()) + ", my commands are:"];
    for t in (.tell_filters())
        o = [@o, (("  \"" + (t[1])) + "\" which calls ") + tostr(t[2])];
    .lsay(o);
    
    // $# Edited 05 Nov 1995 14:08 Lynx ($lynx)
};

public method .lsay() {
    arg what;
    
    what = [((.name()) + " says, \"") + (what[1]), @what.subrange(2)];
    (.location()).announce(what);
    
    // $# Edited 05 Nov 1995 14:09 Lynx ($lynx)
};

public method .follow() {
    arg who;
    
    if (type(who) == 'string)
        who = $object_lib.to_dbref(who);
    if (type(who) != 'objnum)
        throw(~objnf, ("Object (" + who) + ") unknown.");
    following = who;
};

public method .event() {
    arg frob;
    
    if (class(frob) != $movement_event)
        return;
    (frob.exit()).go_through();
};


new object $core: $root;

var $root child_index = 4;
var $root inited = 1;
var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'core, 'variables];
var $root owners = [$core];
var $root managed = [$core];
var $root manager = $core;
var $root owned = [$core];


new object $libraries: $core;

var $root child_index = 9;
var $root fertile = 1;
var $root inited = 1;
var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'fertile, 'core, 'variables];
var $root manager = $libraries;
var $root managed = [$libraries];
var $root owners = [$libraries];
var $root owned = [$libraries];


new object $dictionary: $libraries;

var $root inited = 1;
var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'variables, 'core];
var $root manager = $dictionary;
var $root managed = [$dictionary];
var $root owners = [$dictionary];
var $root owned = [$dictionary];

public method .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 method .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 method .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 method .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 method .nunion() {
    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.
    if (((dict1.keys()).length()) < ((dict2.keys()).length())) {
        for key in (dict1)
            dict2 = dict2.add(key[1], key[2]);
        return dict2;
    } else {
        for key in (dict2)
            dict1 = dict1.add(key[1], key[2]);
        return dict1;
    }
    
    // $#Edited: 30 Jun 96 22:52 $jenner
};

public method .values(): native;

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

public method .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 method .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 method .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 method .invert() {
    arg dict;
    var inverted, x;
    
    // Invert a dict (keys<->values)
    inverted = #[];
    for x in (dict.keys())
        inverted = inverted.add(dict[x], x);
    return inverted;
};

public method .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 " + toliteral(key)) + " (") + toliteral(value)) + ") is not a list.");
    if (value)
        value = [@value, elem];
    else
        value = [elem];
    return dict.add(key, value);
    
    // $#Edited: 13 Jun 96 01:24 $levi
};

public method .del_elem() {
    arg dict, key, elem;
    var value;
    
    value = (| dict[key] |);
    if ((type(value) != 'list) && (type(value) != 'error))
        throw(~type, ((("Value for key " + toliteral(key)) + " (") + toliteral(value)) + ") is not a list.");
    value = value.setremove(elem);
    if (!value)
        return dict.del(key);
    return dict.add(key, value);
    
    // $#Edited: 13 Jun 96 01:25 $levi
};

public method .add(): native;

public method .del(): native;

public method .keys(): native;

public method .add_elem_union() {
    arg dict, key, elem;
    var value;
    
    value = (| dict[key] |);
    if (value)
        value = setadd(value, elem);
    else
        value = [elem];
    return dict_add(dict, key, value);
};

public method .contains(): native;

public method .to_trie() {
    arg dict;
    var i, trie;
    
    trie = (<$trie, [0, ""]>);
    for i in (dict) {
        trie = trie.add(i[1], i[2]);
        refresh();
    }
    return trie;
    
    // $#Edited: 24 Apr 96 16:59 Jenner ($jenner)
};

public method .union(): native {
    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 method .ordered_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.  
    // This one is guaranteed to pass the keys from argument1 to the argument2. It is more suitable for argument parsing.
    for key in (dict1)
        dict2 = dict2.add(key[1], key[2]);
    return dict2;
    
    // $#Copied 05 Jul 96 00:58 from $dictionary.union() by $jenner
};

public method .setadd_elem() {
    arg dict, key, elem;
    var value;
    
    value = (| dict[key] |);
    if (value)
        value = setadd(value, elem);
    else
        value = [elem];
    return dict_add(dict, key, value);
    
    // $#Edited: 14 Jul 96 16:30 $brandon
};


new object $time_root: $libraries;

var $root inited = 1;
var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'variables, '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 hours_per_day = 24;
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;
var $root manager = $time_root;
var $time_root time_units = [[31536000, "year", "years", "yr", "yrs"], [2628000, "month", "months", "mo", "mos"], [604800, "week", "weeks", "wk", "wks"], [86400, "day", "days", "dy", "dys"], [3600, "hour", "hours", "hr", "hrs"], [60, "minute", "minutes", "min", "mins"], [1, "second", "seconds", "sec", "secs"]];
var $root managed = [$time_root];
var $root owners = [$time_root];
var $root owned = [$time_root];

public method .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 method .hr_min_sec() {
    arg [time];
    
    // returns a list: ["hh", "mm", "ss"]
    return [tostr(.hour()), tostr(.minute()), tostr(.second())];
};

public method .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 method .ctime() {
    arg [time];
    
    time = [@time, .time()][1];
    if (!standard)
        return "still working on this";
    else
        return ctime(time);
    
    // $#Moved 18 Sep 96 10:21 from $time_root.ctime() by $admin_brad
    // $#Moved 18 Sep 96 10:30 from $time.ctime() by $admin_brad
};

public method .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 method .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 method .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 method .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 method .minute_str() {
    arg [args];
    
    // will call $integer.int_to_name
    return 'null;
};

public method .hour_str() {
    arg [args];
    
    // will call $integer.int_to_name
    return 'null;
};

public method .second_str() {
    arg [args];
    
    // will call $integer.int_to_name
    return 'null;
};

public method .day_str() {
    arg [time];
    
    time = [@time, .time()][1];
    return tostr(.day());
};

public method .months() {
    return months;
};

public method .days() {
    return days;
};

public method .secs_per_min() {
    return secs_per_min;
};

public method .secs_per_hour() {
    return secs_per_hour;
};

public method .secs_per_day() {
    return secs_per_day;
};

public method .secs_per_week() {
    return secs_per_week;
};

public method .secs_per_year() {
    return secs_per_year;
};

public method .from_english() {
    arg string;
    var words, len, nsec, n, i, entry, unit, ok;
    
    words = string.explode();
    words = words.setremove_all("and");
    len = words.length();
    if (len % 2)
        throw(~args, "Invalid time.");
    nsec = (n = 0);
    for i in [1 .. len] {
        if ((i % 2) == 1) {
            if ((words[i]).is_numeric())
                n = (words[i]).to_number();
            else if ((words[i]) in ["a", "an"])
                n = 1;
            else if ((words[i]) == "no")
                n = 0;
            else
                throw(~invarg, "Invalid time.");
        } else {
            unit = words[i];
            unit = unit.strip(",");
            ok = 0;
            for entry in (time_units) {
                if (unit in (entry.subrange(2))) {
                    ok++;
                    nsec += (entry[1]) * n;
                    break;
                }
            }
            if (!ok)
                throw(~invarg, "Invalid time.");
        }
    }
    return nsec;
    
    // $#Edited: 24 Aug 96 17:25 $brian
    // $#Copied 24 Aug 96 17:36 from $brian.from_english() by $jenner
};


new object $dark_time: $time_root;

var $root inited = 1;
var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'variables, 'core];
var $time_root hours_per_day = 21;
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;
var $root manager = $dark_time;
var $root managed = [$dark_time];
var $root owners = [$dark_time];
var $root owned = [$dark_time];

public method .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 method .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"));
};


new object $time: $time_root;

var $root inited = 1;
var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'variables, 'core];
var $time months = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
var $time days = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
var $root manager = $time;
var $time_root standard = 1;
var $root managed = [$time];
var $root owners = [$time];
var $root owned = [$time];

public method .format(): native;

public method .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 method .dhms() {
    arg secs, [long];
    var ret_str, x;
    
    if (long)
        long = 1;
    if (secs > 86400) {
        x = secs / 86400;
        ret_str = x + (long ? " day" + ((x < 2) ? "" : "s") : "d");
    } else if (secs > 3600) {
        x = secs / 3600;
        ret_str = x + (long ? " hr" + ((x < 2) ? "" : "s") : "h");
    } else if (secs > 60) {
        x = secs / 60;
        ret_str = x + (long ? " min" + ((x < 2) ? "" : "s") : "m");
    } else {
        ret_str = secs + (long ? " sec" + ((secs < 2) ? "" : "s") : "s");
    }
    return ret_str;
};

public method .ldate() {
    arg [args];
    var time, how, sep, lt;
    
    // takes a bunch of numbers and returns info depending upon what the
    // symbol is.  Time is optional, so is how for that matter.
    // 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) 'dash/'slash
    args = [@args, 'long];
    if (type(args[1]) != 'integer) {
        time = time();
        how = args[1];
    } else {
        time = args[1];
        how = args[2];
    }
    sep = ('slash in args) ? "/" : "-";
    switch (how) {
        case 'long:
            return .format("%A, %B %d, %Y");
        case 'noyear:
            return .format("%A, %B %d");
        case 'short:
            return .format("%a, %b %d");
        case 'date:
            return .format(((("%d" + sep) + "%h") + sep) + "%Y");
    }
};

public method .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 method .convert() {
    arg [time];
    
    return [@time, time()][1];
    
    // $#Edited: 18 Sep 96 10:29 $admin_brad
};

public method .time() {
    return .convert();
    
    // $#Moved 18 Sep 96 10:28 from $time_root.time() by $admin_brad
};

public method .hr_min_sec() {
    arg [time];
    var ctime;
    
    ctime = .ctime(@time);
    return [ctime.subrange(12, 2), ctime.subrange(15, 2), ctime.subrange(18, 2)];
};

public method .to_english() {
    arg time, [reftime];
    var times, words, x, ctime, mnths, month, year, days, out, lt, rrk;
    
    // 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 = [];
    for x in ([60, 60, 24]) {
        times = [time % x, @times];
        time = time / x;
    }
    mnths = 0;
    lt = localtime(reftime);
    month = lt[6];
    if ((lt[7]) < 100)
        year = 1900 + (lt[7]);
    else
        year = lt[7];
    days = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month];
    while (time >= (days + ((month == 2) && (((year % 4) == 0) && (!((year % 400) in [100, 200, 300])))))) {
        time -= days;
        mnths++;
        month++;
        if (month > 12) {
            year++;
            month = 1;
        }
    }
    times = [mnths / 12, mnths % 12, time, @times];
    out = [];
    for x in [1 .. 6] {
        if ((times[x]) > 0)
            out += [(((times[x]) + " ") + (words[x])) + (((times[x]) == 1) ? "" : "s")];
    }
    return out.to_english();
    
    // $#Edited: 23 Jul 96 09:01 $brandon
};

public method .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));
    }
};


new object $buffer: $libraries;

var $root trusted = [];
var $root inited = 1;
var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'variables, 'core];
var $root manager = $buffer;
var $root managed = [$buffer];
var $root owners = [$buffer];
var $root owned = [$buffer];

public method .to_list() {
    arg buf;
    var idx, list;
    
    list = [];
    for idx in [1 .. buf.length()]
        list = list + [buf[idx]];
    return list;
};

public method .from_list() {
    arg list;
    var buf, x;
    
    buf = `[];
    for x in [1 .. list.length()]
        buf = buf.add(list[x]);
    return buf;
};

public method .length(): native;

public method .to_string(): native;

public method .to_strings(): native;

public method .from_string(): native;

public method .from_strings(): native;

public method .replace(): native;

public method .subrange(): native;

public method .to_veil_pkts(): native;

public method .from_veil_pkts(): native;

public method .break_lines() {
    arg buf;
    var i, sub, out;
    
    out = [];
    while ((i = 10 in buf)) {
        sub = subbuf(buf, 1, i - 1);
        buf = subbuf(buf, i + 1);
        while ((i = 13 in sub)) {
            if (buflen(sub) == i)
                sub = subbuf(sub, 1, i - 1);
            else
                sub = subbuf(sub, 1, i - 1) + subbuf(sub, i + 1);
        }
        out += [sub];
    }
    if (buf) {
        while ((i = 13 in buf)) {
            if (buflen(buf) == i)
                buf = subbuf(buf, 1, i - 1);
            else
                buf = subbuf(buf, 1, i - 1) + subbuf(buf, i + 1);
        }
        if (buf)
            out += [buf];
    }
    return out;
    
    // $#Edited: 26 Aug 96 20:29 $brandon
};


new object $string: $libraries;

var $root inited = 1;
var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'variables, 'core];
var $string alphabet = "abcdefghijklmnopqrstuvwxyz";
var $string numbers = "1234567890";
var $string non_alphanumeric = "!@#$%^&*()_+-=~`'{}[]|/?\",.<>;: ";
var $root manager = $string;
var $root managed = [$string];
var $root owners = [$string];
var $root owned = [$string];

public method .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 method .fill() {
    arg n, [args];
    var fill, x;
    
    // same as pad("", n, [args]);
    fill = [@args, " "][1];
    return "".pad(n, fill);
};

public method .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((len - textlen) - padlen, rfill) : "");
    else
        return (len > 0) ? text : (text.pad(len));
    
    // $#Edited: 30 Jul 96 18:26 $jenner
};

public method .trim(): native {
    arg str, [dir];
    var reg, range;
    
    dir = [@dir, 'both][1];
    if (dir in ['left, 'both])
        str = strsed(str, "^ *", "");
    if (dir in ['right, 'both])
        str = strsed(str, " *$", "");
    return str;
};

public method .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 = str.match_pattern(sep);
        if (result) {
            list = list + [result[1]];
            str = result[2];
        } else {
            return list + [str];
        }
    }
};

public method .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 method .alphabet() {
    return alphabet;
};

public method .numbers() {
    return numbers;
};

public method .capitalize(): native;

public method .is_numeric() {
    arg string;
    
    return toint(string) || (string == "0");
};

public method .a_or_an() {
    arg string;
    
    if (((string[1]).lowercase()) in "aeiou")
        return "an";
    return "a";
};

public method .strip() {
    arg string, [strip];
    var new_str, char;
    
    new_str = "";
    if (!strip)
        strip = "!@#$%^&*()_+-=~`'{}[]|/?\"\,.<>;: ";
    else
        strip = strip[1];
    for char in [1 .. strlen(string)] {
        if (!((string[char]) in strip))
            new_str = new_str + (string[char]);
    }
    return new_str;
};

public method .non_alphanumeric() {
    return non_alphanumeric;
};

public method .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 method .replace(): native;

public method .explode_english_list() {
    arg line, [opts];
    var x, output, tmp;
    
    if (!line)
        return [];
    
    // explodes an english list ("foo, bar and zoo").
    line = line.explode(",");
    output = [];
    for x in (line) {
        x = .trim(x);
        if ((| x.subrange(1, 4) |) == "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.join(line.subrange(x + 1));
                if (tmp)
                    output = [@output, tmp];
    
                // only bother with the first "and"
                break;
            }
            tmp = (tmp + (tmp ? " " : "")) + (line[x]);
        }
    }
    return output;
};

public method .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 = str.match_pattern(pattern);
        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 method .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), " ");
        if (cutoff <= (prefix.length())) {
            output += "\n" + (string.subrange(1, length));
            string = prefix + (string.subrange(length + 1));
        } else {
            output += "\n" + (string.subrange(1, cutoff - 1));
            string = prefix + (string.subrange(cutoff + 1));
        }
    }
    return (output ? (output.subrange(3)) + "\n" : "") + string;
    
    // $#Edited: 16 Jul 96 20:31 $jenner
};

public method .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 method .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.name())) + " ";
            else
                output = ((output + tag) + (matches[x])) + " ";
        } else {
            output = output + x;
        }
    }
    return output.subrange(1, (output.length()) - 1);
    
    // $# Edited 05 Nov 1995 14:02 Lynx ($lynx)
};

public method .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 method .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 method .is_boolean() {
    arg str;
    
    if (("yes".match_begin(str)) || (("true".match_begin(str)) || (str == "1")))
        return 1;
    else if (("no".match_begin(str)) || (("false".match_begin(str)) || (str == "0")))
        return 0;
    return -1;
};

public method .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 method .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 method .explode(): native;

public method .match_template(): native;

public method .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 method .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 method .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 method .match_pattern(): native;

public method .match_regexp(): native;

public method .to_number() {
    arg str;
    
    if (str.is_numeric())
        return toint(str);
    throw(~nonum, ("\"" + str) + "\" is not a number.");
};

public method .toliteral() {
    arg [args];
    
    return (> toliteral(@args) <);
};

public method .last() {
    arg str;
    
    return str[str.length()];
};

public method .explode_list() {
    arg str;
    
    if ("," in str)
        return str.explode_english_list();
    else
        return str.explode();
};

public method .onespace() {
    arg str;
    var sub;
    
    if ((sub = "  +".sed(str, " ", "g")))
        str = sub;
    return str;
};

public method .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 method .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 method .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 method .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 method .strip_article() {
    arg str;
    
    return strsed(str, "^(an|a|the)  *", "", "g");
};

public method .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 method .echo() {
    arg str;
    
    return str;
};

public method .sub() {
    arg regexp, string;
    var match, m, complete, out;
    
    match = string.match_regexp(regexp);
    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 method .unquote() {
    arg str;
    
    if (str && (((str[1]) == "\"") && ((str[str.length()]) == "\"")))
        return str.subrange(2, (str.length()) - 2);
    return str;
};

public method .to_buffer() {
    arg string;
    
    return (> $buffer.from_string(string) <);
};

public method .pad(): native;

public method .valid_method_name() {
    arg str;
    
    return ((.strip_others(str, (alphabet + numbers) + "_")).length()) == (str.length());
};

public method .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 method .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), " ");
        if (cutoff <= (prefix.length())) {
            output = [@output, string.subrange(1, length)];
            string = prefix + (string.subrange(length + 1));
        } else {
            output = [@output, string.subrange(1, cutoff - 1)];
            string = prefix + (string.subrange(cutoff + 1));
        }
    }
    return [@output, string];
    
    // $#Edited: 28 Mar 96 08:26 Jenner ($jenner)
};

public method .length(): native;

public method .subrange(): native;

public method .match_begin(): native;

public method .crypt(): native;

public method .uppercase(): native;

public method .lowercase(): native;

public method .compare(): native;

public method .format(): native;

public method .to_symbol() {
    arg str;
    
    str = str.strip();
    return (> tosym(str) <);
};

public method .valid_ident() {
    arg str;
    
    return (| tosym(str) |) || 0;
};

public method .regexp(): native;

public method .sed(): native;

public method .split(): native;


new object $command_lib: $libraries;

var $root manager = $command_lib;
var $root inited = 1;
var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'variables, 'core];
var $command_lib arg_types = ["this", "any", "any:*", "object", "object:*", "user", "user:*", "descendant of *", "descendant of *:*", "number", "number:*", "objref", "objref:*"];
var $root managed = [$command_lib];
var $root owners = [$command_lib];
var $root owned = [$command_lib];

public method .get_argument_type() {
    arg type;
    var a, m, opts, o, result, is_list;
    
    o = $object_lib;
    is_list = 0;
    if ((m = type.match_template("list *"))) {
        type = m[2];
        is_list = 1;
    }
    for a in (arg_types) {
        m = match_pattern(type, a);
        if (type(m) == 'list) {
            switch (a) {
                case "descendant of *":
                    result = ['descendant, [(> o.to_dbref(m[1]) <)]];
                case "descendant of *:*":
                    opts = ._parse_option_templates(m[2]);
                    result = ['descendant, [(> o.to_dbref(m[1]) <)] + opts];
                case "number:*", "objref:*", "any:*", "object:*", "user:*":
                    opts = ._parse_option_templates(m[1]);
                    result = [tosym(((a.explode(":"))[1]) + "_opt"), opts];
                default:
                    result = [tosym(a), []];
            }
            return is_list ? ['list, result] : result;
        }
    }
    throw(~command, ("Invalid argument type \"" + type) + "\"");
    
    // $#Edited: 03 Aug 96 18:00 $jenner
    // $#Edited: 06 Aug 96 06:59 $jenner
};

public method .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 method .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)).join()];
    return [cmd, relations];
    
    // $#Edited: 18 Jul 96 14:56 $levi
};

public method .parse_relation() {
    arg left, right;
    var x, str, out;
    
    left = .break_cards(left);
    right = .break_cards(right);
    if ((((left[2]).length()) != ((right[2]).length())) || (!((left[2]).set_equal(right[2]))))
        throw(~invrel, "Left side cards are not equal to the right side.");
    str = "";
    for x in (left[1]) {
        if (type(x) == 'string)
            str += x;
        else if (str && ((str[strlen(str)]) == "*"))
            str += " *";
        else
            str += "*";
    }
    out = [str, @left];
    str = "";
    for x in (right[1]) {
        if (type(x) == 'string)
            str += x;
        else
            str += "*";
    }
    return [out, [str, @right]];
};

public method .unparse_shortcut() {
    arg s;
    var x, line;
    
    line = "";
    for x in ((s[2])[2]) {
        if (type(x) == 'string) {
            if (x == "")
                x = "*";
            line = (line + (line ? " " : "")) + x;
        } else {
            line = ((line + (line ? " " : "")) + "%") + tostr(x);
        }
    }
    return ((((("\"" + (s[1])) + "\"").left(10)) + " => \"") + line) + "\"";
    
    // $# Edited 22 Oct 1995 14:48 Lynx ($lynx)
};

public method .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 method .format_commands_short() {
    arg cmds, type, len;
    var def, name, c, cdef, line, o, cs, dname;
    
    o = [];
    for def in (cmds.keys()) {
        o += [((type + " commands on ") + (def.name())) + ":"];
        cs = [];
        for cdef in (cmds[def]) {
            for c in (cdef[2])
                cs = [@cs, ("\"" + (c[3])) + "\""];
        }
        o += ((cs.msort()).lcolumnize(len - 2, " ")).prefix("  ");
    }
    return o;
};

public method .break_cards() {
    arg str;
    var card, reg, i, x, cards, out, s;
    
    out = (cards = []);
    while ((reg = match_regexp(str, "(%[0-9]+)"))) {
        if (((reg[2])[1]) != 1)
            out = out + [@.break_wildcards(str.subrange(1, ((reg[2])[1]) - 1))];
        card = substr(str, @reg[2]);
        str = substr(str, (reg[2]).sum());
        if (!((card[2]).is_numeric()))
            throw(~invcard, "Argument cards must be numeric.");
        card = toint(substr(card, 2));
        cards += [card];
        out += [card];
    }
    if (str)
        out += [str];
    return [out, cards];
};

public method .format_relation() {
    arg relation;
    var x, str;
    
    str = "";
    for x in (relation) {
        if (type(x) == 'integer)
            str = (str + "%") + tostr(x);
        else
            str = str + x;
    }
    return str;
    
    // $# Edited 18 Oct 1995 13:03 Lynx ($lynx)
};

public method .break_wildcards() {
    arg str;
    var out, i, s;
    
    out = [];
    while ((i = "*" in str)) {
        out += [substr(str, 1, i - 1), ""];
        str = str.subrange(i + 1);
    }
    if (str)
        out = out + [str];
    return out;
};

public method ._parse_option_templates() {
    arg opt;
    var reg, out;
    
    out = [];
    opt = strsed(opt, "^ *", "");
    while (opt) {
        if ((reg = regexp(opt, "^[\+-]([^= ]*)=([^ ]+) *(.*)"))) {
            opt = reg[3];
            out += [delete(reg, 3)];
        } else if ((reg = regexp(opt, "^[\+-]([^ ]+) *(.*)"))) {
            opt = reg[2];
            out += [[reg[1]]];
        } else {
            throw(~invopt, "Option templates must begin with '+' or '-'");
        }
        opt = strsed(opt, "^ *", "");
    }
    return out;
};

public method .arg_types() {
    return arg_types;
};


new object $http_lib: $libraries;

var $root inited = 1;
var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'variables, '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"], ["decompile", "decompile?$http_lib.decompile"], ["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";
var $root manager = $http_lib;
var $root managed = [$http_lib];
var $root owners = [$http_lib];
var $root owned = [$http_lib];

public method ._html_traceback() {
    arg type, what, [more];
    var line;
    
    switch (type) {
        case 'function:
            return ("function <tt>" + what) + "()</tt>";
        case 'opcode:
            return ("operator <tt>" + what) + "</tt>";
        default:
            line = (((more[2]) + ".") + what) + "()";
            if ((more[1]) != (more[2]))
                line = ((line + " (") + (more[1])) + ") ";
            line = (line + " line ") + (more[3]);
            return line;
    }
    
    // $#Edited: 13 Jun 96 00:53 $levi
};

public method .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 method .make_obj_show_href() {
    arg obj, [name];
    var line, oname;
    
    name = [@name, ("<code>" + obj) + "</code>"][1];
    return ((("<a href=\"/bin/show?" + obj) + "\">") + name) + "</a>";
};

public method .make_method_href() {
    arg m;
    
    return ((((((("<a href=\"/bin/decompile?" + (m[1])) + ".") + (m[2])) + "()\">.") + (m[2])) + "(") + (m[3])) + ")</a>";
};

public method .make_object_href() {
    arg obj;
    
    return ((("<code><a href=\"/bin/object?" + obj) + "\">") + (obj.namef('xref))) + "</a></code>";
};

public method .bin_decompile() {
    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.ref(args[1], $environment);
        name = (> tosym(ref[4]) <);
        obj = ref[3];
        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 + ".") + name) + "()";
        out = [("<head><title>" + str_ref) + "</title></head>", ("<body><center><h1>" + str_ref) + "</h1></center>", "<hr><pre>", @code, "</pre>"];
    } with {
        switch (error()) {
            case ~type:
                return [400, ((("Invalid method reference " + obj) + ".") + name) + "()"];
            case ~methodnf:
                line = ((obj + ".") + name) + "()";
                return [400, .response(400, line + " not found.")];
            default:
                return [400, .response(400, (traceback()[1])[2])];
        }
    }
    return [200, out];
    
    // $#Edited: 14 Jul 96 01:29 $levi
};

public method .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.mmap('hname);
    namel = [];
    for x in (who.mmap('name))
        namel = [@namel, x.length()];
    cols = (namel.max()) + 1;
    if (cols < 5)
        cols = 5;
    times = who.mmap('connected_time);
    cols = [cols, (times.element_maxlength()) + 1];
    if ((cols[2]) < 7)
        cols = [cols[1], 7];
    idle = who.mmap('idle_time);
    cols = [@cols, (idle.element_maxlength()) + 1];
    if ((cols[3]) < 5)
        cols = cols.replace(3, 5);
    realm = who.mmap('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 method .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 method .gateways() {
    return gateways;
};

public method .bin_display() {
    arg [args];
    var out, obj, what;
    
    if (!args)
        return [400, .response(400, "Must specify an object")];
    args = (args[1]).explode("&");
    what = [];
    if ("variables" in args)
        what = setadd(what, 'variables);
    if ("methods" in args)
        what = setadd(what, 'methods);
    obj = (| $object_lib.to_dbref(args[1]) |);
    if (!obj)
        return [404, .response(404, ("Unable to find object \"" + (args[1])) + "\"")];
    return [200, .show_object(obj, what)];
};

public method .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 method .http_methods() {
    return http_methods;
};

public method .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 method .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 method .make_href() {
    arg obj, [args];
    var line, oname, method, name;
    
    oname = tostr(obj);
    name = [@args, ("<code>" + oname) + "</code>"][1];
    method = [@args, "/bin/show?" + oname, "/bin/show?" + oname][2];
    return ((("<a href=\"" + method) + "\">") + name) + "</a>";
};

public method .make_href_static() {
    arg obj, [args];
    var line, oname, method, name;
    
    oname = tostr(obj);
    name = [@args, ("<code>" + oname) + "</code>"][1];
    method = [@args, "/bin/show?" + oname, "/bin/show?" + oname][2];
    return ((("<a href=\"" + method) + "\">") + name) + "</a>";
};

public method .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 method .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.search(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 method .bin_see() {
    arg [path];
    
    return [400, .response(400, "VRML support is pending completion, sorry!")];
};

public method .bin_project() {
    arg [path];
    var project, name;
    
    if (!path)
        return [200, $projects.list_projects()];
    name = path.join();
    project = (| $object_lib.to_dbref(path.join()) |);
    if (!project)
        return [400, .respose(400, ("<hr>Unable to find project \"" + name) + "\".")];
    return [200, project.generate_html()];
    
    // $#Edited: 18 Jul 96 14:56 $levi
};

public method .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
        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 = $parse_lib.filter_ctext(node.body(), #[['formatter, $j_html_format], ['node, tostr(node)]]);
    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 + cout) + tail];
    
    // $# Edited 20 Oct 1995 14:06 Lynx ($lynx)
};

public method .process_text() {
    arg what;
    var out, l, b;
    
    switch (type(what)) {
        case 'frob:
            return $parse_lib.filter_ctext(what, #[['formatter, $j_html_format]]);
        case 'list:
            out = `[];
            for l in (what)
                out += .process_text(l);
            return out;
        case 'string:
            // "<br>" == `[60, 98, 114, 62]
            return `[60, 98, 114, 62] + ($buffer.from_string(what));
    }
    
    // $# Edited 20 Oct 1995 14:07 Lynx ($lynx)
};

public method .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 method .html_version() {
    return html_version;
};

public method .build_page() {
    arg what, name;
    var out;
    
    return ((`[60, 104, 101, 97, 100, 62, 60, 116, 105, 116, 108, 101, 62] + str_to_buf(name)) + `[60, 47, 116, 105, 116, 108, 101, 62, 60, 47, 104, 101, 97, 100, 62]) + (.process_text(what));
};

public method .bin_object() {
    arg [args];
    var out, obj, o, line, objs, m;
    
    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])) + "\"")];
    out = [("<head><title>" + (obj.namef('xref))) + "</title></head>", "<body>", ("<h1 align=center>" + (.make_display_href(obj, "&methods"))) + "</h1>"];
    line = "<p align=center><b>Parent(s)</b>: " + ((| .make_object_href((obj.parents())[1]) |) || "(none)");
    for o in ((| (obj.parents()).subrange(2) |) || [])
        line += ", " + (.make_object_href(o));
    out += [line + "</p>", "<pre>"];
    objs = obj.children();
    if (obj) {
        out += ["<p align=center><b>Children:</b></p>", "Name                                 Perms  Size    Manager"];
        for o in (objs) {
            m = o.manager();
            if (!valid(o))
                m = toliteral(m);
            else
                m = .make_object_href(m);
            out += [((((((((("<a href=\"/bin/object?" + o) + "\">") + pad(o.namef('xref), 36)) + "</a>") + " ") + (($object_lib.see_perms(o, ["", ""])).pad(5))) + "  ") + (((o.size()).to_english()).pad(8))) + " ") + m];
        }
    }
    return [200, out + ["</pre>"]];
};

public method ._show_header_objs() {
    arg objs, what;
    var o, line;
    
    if ((objs.length()) > 1) {
        line = (("<b>" + what) + "s</b>: ") + (.make_object_href(objs[1]));
        for o in (objs.subrange(2))
            line += ", " + (.make_object_href(o));
        line += "<br>";
    } else if (objs == 1) {
        line = (("<b>" + what) + "</b>: ") + (.make_object_href(objs[1]));
    } else {
        line = ("<b>" + what) + "</b>: (none)<br>";
    }
    return line;
};

public method ._show_methods() {
    arg obj;
    var methods, types, m, t, out;
    
    types = #[];
    for m in (obj.methods())
        types = types.add_elem(obj.method_access(m), [m] + (obj.method_info(m)));
    
    // hard-listing the types guarantee's their order
    out = [];
    for t in (['root, 'driver, 'public, 'protected, 'private]) {
        if (!(types.contains(t)))
            continue;
        out += [(tostr(t).capitalize()) + " methods:"];
        for m in (types[t])
            out += [strfmt("%5l %4r " + (.make_method_href([obj, m[1], m[2]])), $object_lib.parse_method_flags(m[7]), m[5])];
    }
    return out;
};

public method ._show_variables() {
    arg obj;
    var parent, out, v;
    
    out = [];
    for parent in (obj.data()) {
        if (valid(parent[1])) {
            out += [(parent[1]) + " variables:"];
            if ((parent[1]).has_flag('variables, this())) {
                for v in (parent[2])
                    out += [(("  " + (v[1])) + ": ") + toliteral(v[2])];
            } else {
                out += ["  ** Permission Denied **"];
            }
        } else {
            out += [($object_lib.get_name(parent[1])) + " variables:"];
            for v in (parent[2])
                out += [(("  " + (v[1])) + ": ") + toliteral(v[2])];
        }
        refresh();
    }
    return out;
};

public method .show_object() {
    arg obj, what;
    var out;
    
    out = [("<head><title>ColdC Object Display of " + obj) + "</title></head>", ("<body><h1>" + obj) + "</h1>", ("<b>Perms</b>: " + (((.flags()).prefix("+")).join())) + "<br>", ("<b>Size</b>: " + ((obj.size()).to_english())) + " bytes (on disk)<br>", ("<b>Manager</b>: " + (.make_object_href(obj))) + "<br>", ._show_header_objs(obj.writers('literal), "Writer"), ._show_header_objs(obj.owners(), "Owner"), ._show_header_objs(obj.parents(), "Parent")];
    if (obj.has_ancestor($located))
        out += [("<b>Location</b>: " + (.make_object_href(obj.location()))) + "<br>"];
    out += ["<p><pre>"];
    if ('methods in what) {
        if (!(obj.has_flag('methods, this())))
            out += ["  ** No permission to list methods **"];
        else
            out += ._show_methods(obj);
    } else {
        out += [((((("  <h3><a href=\"/bin/display?" + obj) + "&") + ((['methods] + what).join("&"))) + "\">Display Methods on ") + obj) + "</a></h3>"];
    }
    if ('variables in what) {
        if (!(obj.has_flag('variables, this())))
            out += ["  ** No permission to show variables **"];
        else
            out += ._show_variables(obj);
    } else {
        out += [((((("  <h3><a href=\"/bin/display?" + obj) + "&") + ((what + ['variables]).join("&"))) + "\">Display Variables on ") + obj) + "</a></h3>"];
    }
    return out + ["</pre>"];
    
    // $#Edited: 18 Jul 96 14:56 $levi
};

public method .make_display_href() {
    arg obj, [args];
    
    args = [@args, ""][1];
    return (((("<a href=\"/bin/display?" + obj) + args) + "\">") + (obj.namef('xref))) + "</a>";
};


new object $object_lib: $libraries;

var $root trusted = [];
var $root inited = 1;
var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'variables, 'core];
var $root manager = $object_lib;
var $root managed = [$object_lib];
var $root owners = [$object_lib];
var $root owned = [$object_lib];

public method .to_dbref() {
    arg obj;
    var type, dbref;
    
    type = type(obj);
    switch (type) {
        case 'string:
            if (!obj)
                throw(~invdbref, "Invalid object reference \"\".");
            if ((obj[1]) == "$") {
                obj = obj.subrange(2);
                dbref = (| lookup(tosym(obj)) |);
            } else if ((obj[1]) == "#") {
                obj = obj.subrange(2);
                if ($string.is_numeric(obj))
                    dbref = (| toobjnum(toint(obj)) |);
                else
                    throw(~objnf, ("Cannot find object \"#" + obj) + "\".");
            } else {
                dbref = toint(obj[1]);
                if (dbref || (obj == "0"))
                    dbref = toobjnum(dbref);
                else
                    dbref = (| lookup(obj) |);
            }
            if (!dbref)
                dbref = (> lookup(tosym((obj.replace(" ", "_")).lowercase())) <);
            return dbref;
        case 'objnum:
            return obj;
        default:
            return (> lookup(obj) <);
    }
};

public method .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);
    
    // $#Edited: 01 Dec 95 11:43 Lynx ($lynx)
};

public method .see_perms() {
    arg obj, [args];
    var str, flag, who, encapsulate, flags;
    
    encapsulate = [@args, ["[", "]"]][1];
    str = encapsulate[1];
    flags = obj.flags();
    if ('core in flags) {
        flags = flags.setremove('core);
        str = str + "*";
    } else {
        str = str + "-";
    }
    if ('fertile in flags) {
        flags = flags.setremove('fertile);
        str = str + "f";
    } else {
        str = str + "-";
    }
    if ('methods in flags) {
        flags = flags.setremove('methods);
        str = str + "m";
    } else {
        str = str + "-";
    }
    if ('variables in flags) {
        flags = flags.setremove('variables);
        str = str + "p";
    } else {
        str = str + "-";
    }
    if ('code in flags) {
        flags = flags.setremove('code);
        str = str + "c";
    } else {
        str = str + "-";
    }
    for flag in (flags)
        str = str + ((tostr(flag)[1]).uppercase());
    return str + (encapsulate[2]);
    
    // $#Edited: 06 Jan 96 14:03 Lynx ($lynx)
};

public method .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 method .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 method .parse_method_flags() {
    arg flags;
    
    return (((" " + (('nooverride in flags) ? "!" : "-")) + (('syncronized in flags) ? "s" : "-")) + (('locked in flags) ? "l" : "-")) + (('native in flags) ? "n" : "-");
};

public method .format_object() {
    arg obj, chop;
    var len, line, out, c;
    
    c = obj.created_on();
    out = ["Object:   " + (obj.namef('xref)), "Created:  " + (c ? ctime(c) : "(Before Time)"), (("Quota:    " + (obj.quota())) + " bytes") + ((obj.quota_exempt()) ? " ** exempt **" : ""), "Perms:    " + (((obj.flags()).prefix("+")).join()), ("Size:     " + ((obj.size()).to_english())) + " bytes (on disk)", "Manager:  " + (.get_name(obj.manager(), 'namef, ['xref]))];
    line = obj.writers('literal);
    if ((line.length()) != 1)
        line = "Writers:  " + (line.to_english("(none)"));
    else
        line = "Writer:   " + ((line[1]).namef('xref));
    out += [line];
    line = obj.owners();
    if ((line.length()) != 1)
        line = "Owners:   " + (line.to_english("(none)"));
    else
        line = "Owner:    " + ((line[1]).namef('xref));
    if (chop)
        line = line.chop(chop);
    out += [line];
    line = obj.parents();
    if ((line.length()) > 1)
        line = "Parents:  " + ((line.mmap('namef, 'xref)).to_english());
    else if (!line)
        line = "Parents:  (none)";
    else
        line = "Parent:   " + ((line[1]).namef('xref));
    if (chop)
        line = line.chop(chop);
    out += [line];
    if (obj.has_ancestor($located))
        out += ["Location: " + (.get_name(obj.location(), 'namef, ['xref]))];
    return out;
};

public method .format_method_header() {
    arg obj, method, opt, flags, access;
    var f;
    
    f = "";
    if ('nooverride in flags)
        f = "noover";
    if ('fork in flags)
        f = (f ? f + "," : "") + "fork";
    if ('native in flags)
        f = (f ? f + "," : "") + "native";
    if ('locked in flags)
        f = (f ? f + "," : "") + "locked";
    if (f)
        opt += ((opt ? " " : "") + "+flags=") + f;
    if (access)
        opt += ((opt ? " " : "") + "+access=") + access;
    return (((("@program " + obj) + ".") + method) + "() ") + opt;
    
    // $#Edited: 30 Jun 96 18:47 $lynx
};


new object $integer: $libraries;

var $root inited = 1;
var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'variables, 'core];
var $root manager = $integer;
var $integer ones = ["one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten"];
var $integer teens = ["eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen"];
var $integer tens = ["ten", "twenty", "thirty", "fourty", "fifty", "sixty", "seventy", "eighty", "ninety"];
var $root managed = [$integer];
var $root owners = [$integer];
var $root owned = [$integer];

public method .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 method .parse_range() {
    arg range;
    var r1, r2, reg;
    
    if ("-" in range) {
        reg = regexp(range, "([0-9^#\.]+) *- *([0-9\$\.]+)");
        return [(> ._range_type(reg[1]) <), (> ._range_type(reg[2]) <)];
    } else {
        return [(> ._range_type(range) <), 'none];
    }
    
    // ("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
    // ("#-3") => ('bgn, 3)   -- 'bgn (beginning number), 3
};

public method .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 method .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;
};

public method .to_string() {
    arg [args];
    
    return (> tostr(@args) <);
    
    // $#Edited: 29 Nov 95 19:58 Lynx ($lynx)
};

public method ._range_type() {
    arg type;
    
    switch (type) {
        case "0" .. "9":
            return toint(type);
        case "$":
            return 'end;
        case ".":
            return 'cur;
        case "#", "^":
            return 'bgn;
        default:
            throw(~invrange, ("Invalid range character \"" + type) + "\".", type);
    }
};

public method .and(): native;

public method .or(): native;

public method .xor(): native;

public method .shleft(): native;

public method .shright(): native;

public method .not(): native;

public method .to_english_text() {
    arg number;
    var an, isneg, temp;
    
    an = abs(number);
    isneg = (number < 0) ? "negative " : "";
    if (!number)
        return "zero";
    if (an < 11)
        return isneg + (ones[an]);
    if (an < 20)
        return isneg + (teens[an - 10]);
    if (an < 100)
        return (isneg + (tens[an / 10])) + ((temp = an % 10) ? "-" + (temp.to_english_text()) : "");
    if (an < 1000)
        return ((isneg + (ones[an / 100])) + " hundred") + ((temp = an % 100) ? " " + (temp.to_english_text()) : "");
    if (an < 1000000)
        return ((isneg + ((an / 1000).to_english_text())) + " thousand") + ((temp = an % 1000) ? " " + (temp.to_english_text()) : "");
    if (an < 1000000000)
        return ((isneg + ((an / 1000000).to_english_text())) + " million") + ((temp = an % 1000000) ? " " + (temp.to_english_text()) : "");
    return ((isneg + ((an / 1000000000).to_english_text())) + " billion") + ((temp = an % 1000000) ? " " + (temp.to_english_text()) : "");
    
    // $#Written by: Kipp
    // $#Edited: 25 Aug 96 16:43 $jenner
};


new object $parse_lib: $libraries;

var $root inited = 1;
var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'variables, 'core];
var $parse_lib boolean_strs = [["yes", "true", "1", "on"], ["no", "false", "0", "off"]];
var $root manager = $parse_lib;
var $root managed = [$parse_lib];
var $root owners = [$parse_lib];
var $root owned = [$parse_lib];

public method .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 method .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 {
        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 method .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 method .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]) == "nooverride;")
        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 method .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 = toobjnum(string.subrange(2));
        if (!valid(obj))
            throw(~objnf, "No such object " + string);
        return obj;
    } else {
        return 0;
    }
};

public method .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 method .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 = 'variable;
        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 method .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 ") + error) + " caused by ") + (._traceback(@(traceback[2]).subrange(2)))];
    
    //
    for x in [1 .. (traceback.length()) - 2] {
        if ((x <= lines) || (lines == (-1))) {
            line = ((traceback[x + 2])[1]) + ": ";
            line = line + (._traceback(@(traceback[x + 2]).subrange(2)));
            out = [@out, pre + line];
        }
    }
    return out;
    
    // $#Edited: 13 Jun 96 00:54 $levi
};

public method ._traceback() {
    arg what, [more];
    var line;
    
    if (more) {
        if ((more[1]) == (more[2]))
            return ((((more[1]) + ".") + what) + "() line ") + (more[3]);
        else
            return ((((((more[2]) + ".") + what) + "() (") + (more[1])) + ") line ") + (more[3]);
    } else {
        return what;
    }
    
    // $#Edited: 28 Feb 96 17:42 Lynx ($lynx)
    // $#Edited: 13 Jun 96 01:21 $levi
};

public method .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 method .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 {
                    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 {
                    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 {
                    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 method .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 = 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) != 'objnum)
        obj = (> (match[1]).(match[2])(obj, @match[3]) <);
    if (def && (type(def) != 'objnum))
        def = (> (match[1]).(match[2])(def, @match[3]) <);
    else
        def = obj;
    return out + [obj, def, member];
};

public method .range() {
    arg str;
    var out;
    
    out = str.split(" *- *");
    if ((out.length()) == 1) {
        if ("," in str)
            return ['specific, str];
        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 method ._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 method .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 (part.match_template(templates[t])) {
                    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 method .buildref() {
    arg type, obj, def, name, [ignore];
    var line;
    
    line = tostr(obj);
    if (obj != def)
        line += ("<" + def) + ">";
    if (type == 'object)
        return line;
    if (type == 'method)
        return ((line + ".") + tostr(name)) + "()";
    if (type == 'variable)
        return (line + ",") + tostr(name);
};

public method .filter_ctext() {
    arg what, [defaults];
    var dic, output;
    
    dic = ([@defaults, #[]][1]).ordered_union(#[['receiver, sender()], ['time, 'post], ['formatter, $j_telnet_format]]);
    switch (class(what)) {
        case $j_ctext_frob:
            output = (what.set_vars(dic)).format();
        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;
    
    // $#Edited: 01 Aug 96 21:50 $jenner
};

public method .ref() {
    arg str, [args];
    var def, me, obj, reg, member, match, type, second;
    
    me = [@args, sender()][1];
    if ((args.length()) > 1)
        match = args[2];
    else
        match = [me, 'match_environment, []];
    if (str == ".") {
        // shortcut
        obj = (> (match[1]).(match[2])("", @match[3]) <);
        return ['object, obj, obj, 0, 0];
    }
    if ((reg = regexp(str, "^(.*)<([^>]*)>(.*)$"))) {
        def = (> (match[1]).(match[2])(reg[2], @match[3]) <);
        str = (reg[1]) + (reg[3]);
    }
    if ((reg = regexp(str, "([^\.,]*)([\.,]+)([^\( ]*)"))) {
        obj = reg[1];
        member = reg[3];
        type = reg[2];
        if (((type.length()) > 1) && (((type[1]) == ".") && (!obj))) {
            type = type.subrange(2);
            obj = (> (match[1]).(match[2])("", @match[3]) <);
        } else {
            obj = obj ? (> (match[1]).(match[2])(obj, @match[3]) <) : me;
        }
        if ("." in type) {
            if ("," in type)
                second = 'variable;
            type = 'method;
        } else {
            type = 'variable;
        }
    } else {
        obj = (> (match[1]).(match[2])(str, @match[3]) <);
        type = 'object;
    }
    return [type, obj, def || obj, member, second];
};

public method .parse_method_access() {
    arg str;
    var t;
    
    for t in (["pub?lic", "pro?tected", "pri?vate", "r?oot", "dr?iver"]) {
        if (match_template(str, t))
            return tosym(t.strip("?"));
    }
    return 'public;
};

public method .parse_method_flags() {
    arg flags;
    var t, out, flag;
    
    out = [];
    for flag in (flags.explode(",")) {
        for t in (["no?override", "l?ocked", "f?ork", "na?tive"]) {
            if (match_template(flag, t))
                out += [tosym(t.strip("?"))];
        }
    }
    return out;
};


new object $code_lib: $libraries;

var $root inited = 1;
var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'variables, '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%six0120\"],(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"]];
var $root manager = $code_lib;
var $root managed = [$code_lib];
var $root owners = [$code_lib];
var $root owned = [$code_lib];

public method .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 " + toliteral(obj)) + " is not a valid object.");
    if (!(param in (obj.variables())))
        throw(~parmnf, ((("Variables " + toliteral(param)) + " is not a parmater on ") + toliteral(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.add_method(set_code, '__set_var);
    
    // Set and delete method
    catch any {
        obj.__set_var(param, val);
        obj.del_method('__set_var);
    } with {
        obj.del_method('__set_var);
        rethrow(error());
    }
    
    // $#Edited: 12 Jun 96 19:57 $levi
};

public method ._set_var() {
    arg param, val;
    
    if (sender() != $code_lib)
        throw(~perm, "Sender not $code_lib");
    return set_var(param, val);
};

public method .quotes() {
    return quotes;
};

public method .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 method .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, [['namef, 'doing], ['connected_time], ['idle_time], ['realm_name]], [['namef, 'doing], ['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];
    
    // if who is empty, only print header
    if (who == 0) {
        len = (| sender().linelen() |) || 79;
        output = [("--- " + title) + " (0) ---"];
        return [@output];
    }
    
    // get values using $list.mmap
    element = $list.mmap(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.mmap(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));
        if ((line.length()) > len)
            line = line.subrange(1, len);
        output = [@output, line];
    }
    return [@output];
    
    // $# Edited 28 Oct 1995 21:09 Lynx ($lynx)
    // $#Edited: 22 Jul 96 18:33 $levi
};

public method .random_quote() {
    var which;
    
    which = random(quotes.length());
    if (which)
        return quotes[which];
    return [];
};

public method .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 = $network.hostname(host);
        if (tmp == "-1")
            return ['invip, user, host];
    } else {
        tmp = $network.ip(host);
        if (tmp == "-1")
            return ['invhostname, user, host];
    }
    return ['valid, user, host];
};

public method .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 method .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]).join();
    flag = (| flag[flag.length()] |) || ["p?roper", "p", 1, ""];
    switch (flag[1]) {
        case "n?ormal":
            article = 'normal;
        case "u?nique":
            article = 'uniq;
        default:
            article = 'prop;
    }
    name = name.explode(",");
    return [[name[1], article], name.subrange(2)];
    
    // $#Edited: 18 Jul 96 14:56 $levi
};

public method .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 method .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 method .check_encrypted() {
    arg cstr, str;
    
    return crypt(str, cstr.subrange(1, 2)) == cstr;
};

public method .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 method .command_argument_types() {
    return command_argument_types;
};

public method .get_command_argument_type() {
    arg type;
    var x, m;
    
    for x in [1 .. command_argument_types.length()] {
        m = type.match_pattern((command_argument_types[x])[2]);
        if (type(m) == 'list)
            return [(command_argument_types[x])[1], m];
    }
    throw(~command, ("Invalid command argument type \"" + type) + "\"");
};

public method .verify_code() {
    arg code, method;
    var line, m, warns;
    
    warns = [];
    method = ("\." + tostr(method)) + "\(";
    for line in [1 .. code.length()] {
        m = (code[line]).match_regexp(method);
        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 method .default_description_flags() {
    return #[['prose, 1], ['contents, 1], ['extra, 1], ['visibility, 1], ['actor, sender()], ['exclude, []], ['brief, 0]];
};

public method .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 = [strfmt(fmt, "#", "Name", "Perms", "Size") + "Manager"];
        col = col * 4;
        for obj in (objs) {
            line = strfmt(fmt, 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;
};

public method .xname() {
    arg obj, type;
    var str;
    
    if (!valid(obj))
        return ("** " + tostr(obj)) + " **";
    if (!(obj.is($user)))
        return obj.name();
    switch (type) {
        case 'doing:
            str = obj.activity('noidle);
            if (!str)
                return obj.name();
            return (((obj.name()) + " (") + str) + ")";
        case 'activity:
            str = obj.activity();
            if (!str)
                return obj.name();
            return (((obj.name()) + " (") + str) + ")";
        case 'titled:
            str = obj.title();
            if (!str)
                return obj.name();
            return ((obj.name()) + ", ") + str;
    }
    return "";
    
    // $# Edited 28 Oct 1995 20:50 Lynx ($lynx)
};

public method .format_name() {
    arg obj;
    
    return (obj.name()) + ((obj.name_aliases()) ? (" (" + ((obj.name_aliases()).to_english())) + ")" : "");
    
    // $#Edited: 01 May 96 19:09 $lynx
};


new object $list: $libraries;

var $root inited = 1;
var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'variables, 'core];
var $root manager = $list;
var $root help_node = #491;
var $root managed = [$list];
var $root owners = [$list];
var $root owned = [$list];

public method .to_english() {
    arg list, [options];
    var empty, and, sep;
    
    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 (join(list.delete(list.length()), sep) + and) + tostr(list[list.length()]);
};

public method .mmap() {
    arg list, method, [args];
    var x;
    
    // call 'method on each object, return results.
    return map x in (list) to (x.(method)(@args));
};

public method .mfilter() {
    arg list, method, [args];
    var x;
    
    // similar to .mmap, but returns a list of objects which returned a
    // true value from 'method.
    return filter x in (list) where (x.method(@args));
    
    // $#Edited: 18 Jul 96 11:37 $jenner
};

public method .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());
    
    // $#Edited: 18 Jul 96 11:37 $jenner
};

public method ._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;
    
    // $#Edited: 18 Jul 96 11:37 $jenner
};

public method .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;
    
    // $#Edited: 18 Jul 96 11:37 $jenner
};

public method .reverse() {
    arg list;
    var i, len;
    
    // .reverse(list)
    // -> list with its elements reversed
    len = (list.length()) + 1;
    return map i in [1 .. len - 1] to (list[len - i]);
    
    // $#Edited: 18 Jul 96 11:57 $jenner
};

public method .compress() {
    arg list;
    var x;
    
    // [a,a,b,b,c,c,d,d] => [a,b,c,d]
    // removes duplicate entries in a list
    return hash x in (list) to ([x, 1]).keys();
    
    // $#Edited: 18 Jul 96 11:37 $jenner
};

public method .last() {
    arg list;
    
    return list[list.length()];
    
    // $#Edited: 18 Jul 96 11:37 $jenner
};

public method .count() {
    arg list, elem;
    var count;
    
    // count of elem in list
    while (elem in list) {
        count = count + 1;
        list = list.subrange((elem in list) + 1);
    }
    return count;
    
    // $#Edited: 18 Jul 96 11:37 $jenner
};

public method .element_maxlength() {
    arg list;
    var elm;
    
    return map elm in (list) to (tostr(elm).length()).max();
    
    // $#Edited: 18 Jul 96 11:37 $jenner
};

public method .nth_element_maxlength() {
    arg lists, element;
    var list;
    
    // Returns longest string whose index is element in one of the lists in
    // lists.
    if (type(element) != 'integer)
        throw(~type, "Second argument is not an integer");
    if (type(lists) != 'list)
        throw(~type, "First argument is not a list");
    return map list in (lists) to (tostr(list[element]).length()).max();
    
    // $#Edited: 18 Jul 96 11:37 $jenner
};

public method .numbered_text() {
    arg text;
    var line;
    
    // receives a list of strings, returns that list with line numbers
    // prepended
    return map line in [1 .. text.length()] to ("%3r: %l".format(line, text[line]));
    
    // $#Edited: 18 Jul 96 11:37 $jenner
};

public method .slice() {
    arg big_list, element;
    var list;
    
    // Return elementh' element of all lists in big_list
    // No type or length checking done for speed purposes.
    return map list in (big_list) to (list[element]);
    
    // $#Edited: 18 Jul 96 11:37 $jenner
};

public method .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;
    
    // $#Edited: 18 Jul 96 11:37 $jenner
};

public method .max() {
    arg list;
    
    return (| max(@list) |) || 0;
    
    // $#Edited: 18 Jul 96 11:37 $jenner
};

public method .min() {
    arg list;
    
    return (| min(@list) |) || 0;
    
    // $#Edited: 18 Jul 96 11:37 $jenner
};

public method .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;
    
    // $#Edited: 18 Jul 96 11:37 $jenner
};

public method .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;
    
    // $#Edited: 18 Jul 96 11:37 $jenner
};

public method ._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;
    }
    
    // $#Edited: 18 Jul 96 11:37 $jenner
};

public method .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;
    
    // $#Edited: 18 Jul 96 11:37 $jenner
};

public method .map_to_english() {
    arg list, method, [args];
    
    // because I (Lynx) am lazy
    return .to_english(.mmap(list, method, @args));
    
    // $#Edited: 18 Jul 96 11:37 $jenner
};

public method .map_to_string() {
    arg list, method, [args];
    
    // because I (Lynx) am lazy
    return .join(.mmap(list, method, @args));
    
    // $#Edited: 18 Jul 96 11:37 $jenner
    // $#Edited: 18 Jul 96 14:56 $levi
};

public method .flatten() {
    arg list;
    var toret, elem;
    
    // [[[x], x], x]   =>   [x, x, x]
    toret = [];
    for elem in (list) {
        if (type(elem) == 'list)
            toret += .flatten(elem);
        else
            toret = toret + [elem];
    }
    return toret;
    
    // $#Edited: 18 Jul 96 11:37 $jenner
};

public method .sum() {
    arg data;
    var ret, i;
    
    // returns a sum of each element in the list.
    ret = data[1];
    for i in (data.subrange(2))
        ret += i;
    return ret;
    
    // $#Edited: 13 Aug 96 20:58 $jenner
};

public method .non_alphanumeric() {
    // returns nun-alphanumeric in a list of characters
    return ["!", "@", "#", "$", "%", "^", "&", "*", "(", ")", "_", "+", "-", "=", "~", "`", "'", "{", "}", "[", "]", "|", "/", "?", "\"", "\\", ",", ".", "<", ">", ";", ":", " "];
    
    // $#Edited: 18 Jul 96 11:37 $jenner
};

public method .numbers() {
    // returns a list of numbers
    return ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"];
    
    // $#Edited: 18 Jul 96 11:37 $jenner
};

public method .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"];
    
    // $#Edited: 18 Jul 96 11:37 $jenner
};

public method .center_lines() {
    arg lines, width, [args];
    var line;
    
    return map line in (lines) to ($string.center(line, width, @args));
    
    // $#Edited: 18 Jul 96 11:37 $jenner
};

public method .to_buffer() {
    arg [args];
    
    return (> $buffer.from_strings(@args) <);
    
    // $#Edited: 18 Jul 96 11:37 $jenner
};

public method .delete(): native;

public method .replace(): native;

public method .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) |) || [];
    
    // $#Edited: 18 Jul 96 11:37 $jenner
};

public method .join(): native {
    arg list, [sep];
    var str, part;
    
    if (!list)
        return "";
    sep = [@sep, " "][1];
    str = tostr(list[1]);
    for part in (list.delete(1))
        str = (str + sep) + tostr(part);
    return str;
};

public method .lmap() {
    arg list, method, [args];
    var x, s;
    
    //call methods for each thing in list on sender()
    s = sender();
    return map x in (list) to (s.(method)(x, @args));
    
    // $#Edited: 18 Jul 96 11:37 $jenner
};

public method .length(): native;

public method .union(): native;

public method .omap() {
    arg list, object, method, [args];
    var obj;
    
    // calls object.method(obj, @args) for each obj in list
    return map obj in (list) to (object.(method)(obj, @args));
    
    // $#Edited: 18 Jul 96 11:37 $jenner
};

public method .del() {
    arg list, element;
    
    return (> list.setremove(element) <);
    
    // $#Edited: 18 Jul 96 11:37 $jenner
};

public method .add() {
    arg list, element;
    
    return (> list.setadd(element) <);
    
    // $#Edited: 18 Jul 96 11:37 $jenner
};

public method .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;
    
    // $#Edited: 18 Jul 96 11:37 $jenner
};

public method .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;
    
    // $#Edited: 18 Jul 96 11:37 $jenner
};

public method .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 == [];
    
    // $#Edited: 18 Jul 96 11:37 $jenner
};

public method .fold() {
    arg list, object, method, [args];
    var i, out;
    
    // apply object.method to a current result and the next element, return the
    // result
    switch (list.length()) {
        case 0:
            return 0;
        case 1:
            return list[1];
    }
    out = list[1];
    for i in (list.subrange(2, (list.length()) - 1))
        out = object.(method)(out, i, @args);
    return out;
    
    // 7-26-95/20:26 Lynx ($lynx), moved from $user_jenner.fold
    // $#Edited: 18 Jul 96 11:37 $jenner
};

public method .setadd(): native;

public method .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;
    
    // $#Edited: 18 Jul 96 11:37 $jenner
};

public method .setremove(): native;

public method .insert(): native;

public method .subrange(): native;

public method .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
    // $#Edited: 18 Jul 96 11:37 $jenner
};

private method ._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 method .prefix() {
    arg list, prefix;
    var elem;
    
    return map elem in (list) to (prefix + elem);
    
    // $#Edited: 18 Jul 96 11:37 $jenner
};

public method .valid_objects() {
    arg list;
    var obj;
    
    return filter obj in (list) where (valid(obj));
    
    // $#Edited: 18 Jul 96 11:37 $jenner
};

public method .grep() {
    arg lines, regexp;
    var line, result, out, reg;
    
    out = [];
    for line in [1 .. lines.length()] {
        reg = (lines[line]).match_regexp(regexp);
        if (reg)
            out = out + [[line, reg, lines[line]]];
    }
    return out;
    
    // $#Edited: 18 Jul 96 11:37 $jenner
};

public method .vcolumnize() {
    arg list, cols, [rest];
    var linelength, sep, width, lines, i, j, line, outlist;
    
    linelength = [@rest, (| sender().linelen() |) || 79][1];
    sep = [@rest, " ", " "][2];
    lines = ((list.length()) / cols) + (((list.length()) % cols) ? 1 : 0);
    width = linelength / cols;
    return ._vcolumnize(list, lines, cols, width, sep);
    
    // $#Edited: 20 Apr 96 20:25 Levi ($levi)
    // $#Copied 21 Sep 96 19:29 from $ball.vcolumn2() by $levi
};

public method .make() {
    arg n, [elt];
    var i;
    
    elt = [@elt, 0][1];
    return map i in [1 .. n] to (elt);
    
    // $#Edited: 19 Apr 96 22:29 Jenner ($jenner)
    // $#Edited: 18 Jul 96 11:37 $jenner
};

public method ._vcolumnize() {
    arg list, lines, cols, width, [other];
    var outlist, line, i, j, sep;
    
    sep = [@other, " "][1];
    width -= sep.length();
    lines = (lines > (list.length())) ? list.length() : lines;
    outlist = [];
    for i in [1 .. lines] {
        line = (list[i]).pad(width);
        for j in [1 .. cols]
            (| (line = (line + sep) + ((list[i + (j * lines)]).pad(width))) |);
        outlist = [@outlist, line];
    }
    return outlist;
    
    // $#Edited: 20 Apr 96 20:23 Levi ($levi)
    // $#Copied 21 Sep 96 19:28 from $ball._vcolumnize() by $levi
};

public method .affix() {
    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;
    
    // $#Edited: 18 Jul 96 11:37 $jenner
};

public method .addkey() {
    arg l, key, val;
    var i;
    
    find i in [1 .. l.length()] where (((l[i])[1]) == key);
    return i ? l.replace(i, [key, val]) : [@l, [key, val]];
    
    // $#Edited: 18 Jul 96 11:50 $jenner
};

public method .delkey() {
    arg l, key;
    var i;
    
    find i in [1 .. l.length()] where (((l[i])[1]) == key);
    return i ? l.delete(i) : l;
    
    // $#Edited: 18 Jul 96 11:50 $jenner
};

public method .getkey() {
    arg l, key;
    var i;
    
    find i in [1 .. l.length()] where (((l[i])[1]) == key) || throw(~keynf, "Key not found.");
    return (l[i])[2];
    
    // $#Edited: 18 Jul 96 11:37 $jenner
};

public method .getkey_index() {
    arg l, key;
    var i;
    
    find i in [1 .. l.length()] where (((l[i])[1]) == key) || throw(~keynf, "Key not found.");
    return i;
    
    // $#Edited: 18 Jul 96 11:37 $jenner
};

public method .setremove_all() {
    arg list, remove;
    
    while (remove in list)
        list = .setremove(list, remove);
    return list;
    
    // $#Edited: 24 Aug 96 17:39 $jenner
};

public method .vcolumnize2() {
    arg list, lines, [rest];
    var linelength, sep, cols, width, i, j, line, outlist;
    
    linelength = [@rest, (| sender().linelen() |) || 79][1];
    sep = [@rest, " ", " "][2];
    cols = ((list.length()) / lines) + (((list.length()) % lines) ? 1 : 0);
    width = linelength / cols;
    return ._vcolumnize(list, lines, cols, width, sep);
    
    // $#Edited: 20 Apr 96 20:24 Levi ($levi)
    // $#Copied 21 Sep 96 19:30 from $ball.vcolumnize() by $levi
};

public method .vcolumnize3() {
    arg list, lines, [rest];
    var linelength, cols, width, i, j, line, outlist;
    
    linelength = [@rest, (| sender().linelen() |) || 79][1];
    cols = ((list.length()) / lines) + (((list.length()) % lines) ? 1 : 0);
    width = linelength / cols;
    outlist = [];
    for i in [1 .. lines] {
        line = "";
        for j in [0 .. cols]
            (| (line = line + ((list[i + (j * lines)]).pad(width))) |);
        outlist = [@outlist, line];
    }
    return outlist;
    
    // $#Edited: 19 Apr 96 23:54 Levi ($levi)
    // 4-19-96/23:55 Levi ($levi), moved from $ball.vcolumnize
    // $#Copied 21 Sep 96 19:30 from $ball.vcolumnize2() by $levi
};

public method .vcolumnize4() {
    arg list, [args];
    var linelength, sep, lines, cols, width, max;
    
    linelength = [@args, (| sender().linelen() |) || 79][1];
    sep = [@args, "", ""][2];
    max = (.element_maxlength(list)) + (sep.length());
    cols = (linelength > max) ? linelength / max : 1;
    width = linelength / cols;
    lines = ((list.length()) / cols) + (((list.length()) % cols) ? 1 : 0);
    return ._vcolumnize(list, lines, cols, width, sep);
    
    // $#Edited: 20 Apr 96 19:56 Levi ($levi)
    // $#Copied 21 Sep 96 19:30 from $ball.vcolumn3() by $levi
};


new object $help_lib: $libraries;

var $root manager = $help_lib;
var $root created_on = 805931416;
var $root inited = 1;
var $root flags = ['methods, 'code, 'variables, 'core];
var $help_lib default_node = $help_summary;
var $help_lib default_index = $help_index_root;
var $help_lib default_indices = [$help_index_root];
var $root managed = [$help_lib];
var $root owners = [$help_lib];
var $root owned = [$help_lib];

public method .default_node() {
    return default_node;
};

public method .default_indices() {
    return default_indices;
    
    // $#Edited: 22 Jul 96 09:59 $jenner
};

public method .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 method .history_cap() {
    return 15;
};

public method .parse_reference() {
    arg str;
    var node, current, indices, l, links;
    
    if ((str[1]) in ["$", "#"]) {
        node = (| $object_lib.to_dbref(str) |);
        if ((!node) || ((!(node.has_ancestor($help_node))) && (!(| (node = node.help_node()).has_ancestor($help_node) |))))
            throw(~nonode, ("\"" + str) + "\" is not descended from $help_node.");
        return node;
    }
    if ((str[1]) == "*") {
        str = str.subrange(2);
    } else {
        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_indices());
    node = 0;
    l = indices.length();
    while (l && (!node)) {
        catch any {
            node = (indices[l]).match_begin(str);
        } with {
            if (error() == ~ambig)
                rethrow(~nonode);
        }
        l = l - 1;
    }
    return node || throw(~nonode, ("Unable to find help on \"" + str) + "\".");
    
    // $#Edited: 30 Jul 96 16:49 $jenner
};

public method .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 method .node_name_in_html() {
    arg node;
    
    return ((("<a href=\"/bin/help?" + node) + "\">") + (node.name())) + "</a>";
};


new object $ctext_lib: $libraries;

var $root manager = $ctext_lib;
var $root owners = [$ctext_lib];
var $root created_on = 799277513;
var $root inited = 1;
var $root flags = ['methods, 'code, 'variables, 'core];
var $root managed = [$ctext_lib];
var $root owned = [$ctext_lib];


new object $misc: $core;

var $root child_index = 33;
var $root fertile = 1;
var $root inited = 1;
var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'fertile, 'core, 'variables];
var $root manager = $misc;
var $root managed = [$misc];
var $root owners = [$misc];
var $root owned = [$misc];


new object $mail_root: $misc;

var $root child_index = 2;
var $root fertile = 1;
var $root trusted = [];
var $root inited = 1;
var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'fertile, 'core, 'variables];
var $root owners = [$mail_root];
var $root manager = $mail_root;
var $root managed = [$mail_root];
var $root owned = [$mail_root];


new object $mail_list: $mail_root, $named;

var $root child_index = 2;
var $root inited = 1;
var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'core, 'variables];
var $named name_aliases = [];
var $named name = ['uniq, "mail_list", "the mail_list"];
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 $root trusted_by = [$mail_db];
var $root manager = $mail_list;
var $root managed = [$mail_list];
var $root owners = [$mail_list];
var $root owned = [$mail_list];

public method .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 method .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 method .list_is_sendable_by() {
    arg who;
    
    if (.is_writable_by(who))
        return 1;
    if (type(senders) == 'list)
        return who in senders;
    return senders;
};

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

public method .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 method .start() {
    if (!(.list_is_readable_by(sender())))
        throw(~perm, ("Sender cannot read " + (.mail_name())) + ".");
    if (mail)
        return mail[1];
    return 0;
};

public method .last_received_on() {
    return last_received_on;
};

public method .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 method .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 method .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 method .notify() {
    (> .perms(sender(), 'manager) <);
    return notify;
};

public method .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);
    
    // $# Edited 23 Oct 1995 12:12 Lynx ($lynx)
};

public method ._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()).name())) + ": ") + (new_mail.subject());
    for who in (notify)
        (| who.tell(line.chop(who.linelen())) |);
    
    // $# Edited 05 Nov 1995 14:04 Lynx ($lynx)
};

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

public method .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 method .senders() {
    (> .perms(sender(), 'manager) <);
    return senders;
};

public method .mail_name() {
    return $mail_lib.mail_name(this());
};

root method .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 method .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 method .mail_list_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 method .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];
};


new object $mail_ui: $mail_list, $user_interfaces;

var $root inited = 1;
var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'core, 'variables];
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 $has_commands local = #[["@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, #[]]]], ["@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, []]]]]]], ["@nn|@next-new", [["@nn|@next-new", "*", "@nn|@next-new <any>", 'next_new_cmd, #[[1, ['any, []]]]]]], ["@mail", [["@mail", "*", "@mail <any>", 'mail_on_cmd, #[[1, ['any, []]]]]]], ["@send", [["@send", "*", "@send <any>", 'send_to_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;
var $root manager = $mail_ui;
var $root managed = [$mail_ui];
var $root owners = [$mail_ui];
var $root owned = [$mail_ui];

protected method .send_to_cmd() {
    arg cmdstr, cmd, str;
    var subj, lists, note, list, x, mail, text, args;
    
    if ((args = match_template(str, "* to *"))) {
        note = args[1];
        args = args[3];
    } else {
        note = "";
        args = str;
    }
    if (args) {
        lists = [];
        for list in (args.explode_english_list()) {
            catch ~listnf {
                list = (> $mail_lib.match_mail_recipient(list) <);
                lists += [list];
            } with {
                .tell(("The list \"" + list) + "\" is invalid.");
            }
        }
        if (!lists)
            return "No lists specified.";
    } else {
        lists = [current['list]];
    }
    
    // get the text of the message
    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;
        if (text == 'engaged)
            return "** Already reading - mail send aborted **";
    }
    subj = .prompt("Subject: ");
    if (subj == "@abort")
        return "** Aborted mail send **";
    if (subj == 'engaged)
        return "** Already reading - mail send aborted **";
    mail = $mail_message.new_mail();
    mail.set_subject(subj);
    mail.set_text(text);
    catch any
        mail.send(@lists);
    with
        return (traceback()[1])[2];
    return "Mail sent.";
};

protected method .mail_read_cmd() {
    arg cmdstr, cmd, str;
    var mail, m, args, lname, list, rng, args;
    
    if ((args = match_template(str, "* on *"))) {
        rng = args[1];
        catch ~listnf
            list = (> $mail_lib.match_mail_recipient(args[3]) <);
        with
            return (traceback()[1])[2];
    } else {
        rng = str;
        list = current['list];
    }
    catch ~perm
        (> .new_list(list) <);
    with
        return (traceback()[1])[2];
    if (((list.mail()).length()) == 0)
        return ("There is no mail on " + (list.mail_name())) + ".";
    if (!rng)
        return ("You must specify a message to read on " + (list.mail_name())) + ".";
    catch ~range {
        if (rng == "next") {
            .read_mail((> list.mail_list_next((subscribed[list])[2]) <), list);
        } else if (rng == "prev") {
            .read_mail((> list.mail_list_prev((subscribed[list])[2]) <), list);
        } else {
            for m in ($mail_lib.range_to_actual($parse_lib.range(rng), current))
                .read_mail(m, list);
        }
    } with {
        return ((("Mail message " + rng) + " does not exist on ") + (list.mail_name())) + ".";
    }
    return "-----";
};

protected method .mail_remove_cmd() {
    arg cmdstr, cmd, str;
    var mail, args, lmail, list, rng, m, x, name, lname, offset, ans;
    
    if ((args = match_template(str, "* on|from *"))) {
        rng = args[1];
        list = args[3];
    } else if (!str) {
        return "You must specify a message and list.";
    } else {
        // grr, be hacky
        args = explode(str);
        if ((args.length()) > 1) {
            list = args.last();
            if (!(| $mail_lib.match_mail_recipient(list) |)) {
                rng = str;
                list = "";
            } else {
                rng = (args.delete(args.length())).join();
            }
        } else {
            rng = str;
            list = "";
        }
    }
    if (!rng)
        return ("You must specify a message to remove from " + lname) + ".";
    if (!list) {
        list = current['list];
        ans = .prompt(("Remove mail from " + (list.mail_name())) + "? ");
        if (type(ans) == 'symbol)
            return;
        if (match_regexp(ans, "no|n"))
            return "Ok, aborting..";
    } else {
        catch ~listnf
            list = (> $mail_lib.match_mail_recipient(list) <);
        with
            return ("The list \"" + list) + "\" is invalid.";
    }
    lname = list.mail_name();
    catch any
        (> .new_list(list) <);
    with
        return (traceback()[1])[2];
    if (rng && ((rng[1]) == "$")) {
        catch ~namenf
            mail = [(> $object_lib.to_dbref(rng) <)];
        with
            return (traceback()[1])[2];
    } else {
        catch ~range
            mail = (> $mail_lib.range_to_actual($parse_lib.range(rng), current) <);
        with
            return (traceback()[1])[2];
    }
    lmail = list.mail();
    if ((mail[1]) != (| lmail[1] |)) {
        offset = (mail[1]) in lmail;
        lmail = sublist(lmail, offset);
        offset--;
    }
    for m in (mail) {
        catch ~perm {
            x = (m in lmail) + offset;
            name = ((((("#" + x) + " \"") + (m.subject())) + "\" (") + m) + ")";
            list.del_mail(m);
            .tell(((("Removed message " + name) + " from ") + (list.mail_name())) + ".");
        } with {
            .tell((traceback()[1])[2]);
        }
    }
};

protected method .read_mail() {
    arg mail, list;
    var loc;
    
    loc = mail in (list.mail());
    .tell(strfmt("Message %l (%l) on %s:", loc, mail.name(), list.mail_name()));
    .tell(mail.format());
    mail.did_read();
    subscribed = subscribed.add(list, [time(), (subscribed[list])[2]]);
    
    // $#Edited: 17 Aug 96 12:41 $jenner
};

protected method .subscribed() {
    return subscribed;
};

protected method .unsubscribe_cmd() {
    arg cmdstr, cmd, str;
    var list, line, mname;
    
    if (!str)
        return .mail_lists_cmd();
    list = $mail_lib.match_mail_recipient(str);
    if (list == this())
        return "You cannot unsubscribe yourself.";
    mname = list.mail_name();
    if (!(list in (subscribed.keys())))
        return "You are not subscribed to " + mname;
    .unsubscribe(list);
    return ("Successfully unsubscribed from " + mname) + ".";
};

protected method .subscribe_cmd() {
    arg cmdstr, cmd, str;
    var list, mname, l, args, line, len, out;
    
    // 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])[1]))
                    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])[1]))
                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) + ".");
};

protected method .subscribe() {
    arg list;
    
    if (!subscribed)
        subscribed = #[];
    subscribed = subscribed.add(list, [time(), 0]);
    (| list.add_sender_to_notification() |);
};

protected method .unsubscribe() {
    arg list;
    
    subscribed = subscribed.del(list);
    (| list.del_sender_from_notification() |);
};

protected method .mail_lists_cmd() {
    arg [args];
    var l, line;
    
    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);
    }
};

protected method .new_list() {
    arg list;
    
    // check here so we can assume later that this error will not bite us
    if (!(subscribed.contains(list))) {
        if (!(list.list_is_readable_by(this())))
            throw(~perm, "You cannot read mail on " + (list.mail_name()));
    }
    
    // set the current list
    if (list != (current['list]))
        current = current.add('list, list);
};

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

protected method .mail_on_cmd() {
    arg cmdstr, cmd, str;
    var args, lmail, mail, rng, start, end, line, list, len, out, m;
    
    if ((args = match_template(str, "* on *"))) {
        rng = args[1];
        list = args[3];
    } else if (match_template(str, "* to *")) {
        return (> .send_to_cmd(cmdstr, cmd, str) <);
    } else {
        rng = "";
        list = str;
    }
    if (!list) {
        list = current['list];
    } else {
        catch ~listnf, ~perm {
            list = (> $mail_lib.match_mail_recipient(list) <);
            (> .new_list(list) <);
        } with {
            return (traceback()[1])[2];
        }
    }
    if (!rng) {
        mail = list.mail();
        end = mail.length();
    
        // magic numbers, yahoo
        if (end > 50) {
            start = end - 50;
            mail = sublist(mail, start);
        } else {
            start = 1;
        }
        rng = (start + "-") + end;
    } else {
        catch ~range
            mail = $mail_lib.range_to_actual($parse_lib.range(rng), current);
        with
            return (traceback()[1])[2];
    }
    if (!mail)
        return "No mail on " + (list.mail_name());
    len = (.linelen()) - 33;
    out = [((("Mail from " + rng) + " on ") + (list.mail_name())) + ":"];
    lmail = list.mail();
    for m in (mail)
        out += [strfmt("%s%3r:%s%*L %14L%11l", (mail == (| (subscribed[list])[2] |)) ? "=>" : "  ", m in lmail, (m.has_read(this())) ? " " : "!", len, m.subject(), $object_lib.get_name(m.from(), 'name), $time.format("%d-%h-%Y", m.time()))];
    .tell(out + ["------"]);
};

protected method .mail_to_cmd() {
    arg cmdstr, cmd, str;
    var subj, lists, note, list, x, mail, text, args;
    
    if ((args = match_template(str, "* to *"))) {
        note = args[1];
        args = args[3];
    } else if ((cmd == "@mail") && (args = match_template(str, "* on *"))) {
        return (> .mail_on_cmd(cmdstr, cmd, str) <);
    } else {
        note = "";
        args = str;
    }
    if (args) {
        lists = [];
        for list in (args.explode_english_list()) {
            catch ~listnf {
                list = (> $mail_lib.match_mail_recipient(list) <);
                lists += [list];
            } with {
                .tell(("The list \"" + list) + "\" is invalid.");
            }
        }
        if (!lists)
            return "No lists specified.";
    } else {
        lists = [current['list]];
    }
    
    // get the text of the message
    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;
        if (text == 'engaged)
            return "** Already reading - mail send aborted **";
    }
    subj = .prompt("Subject: ");
    if (subj == "@abort")
        return "** Aborted mail send **";
    if (subj == 'engaged)
        return "** Already reading - mail send aborted **";
    mail = $mail_message.new_mail();
    mail.set_subject(subj);
    mail.set_text(text);
    catch any
        mail.send(@lists);
    with
        return (traceback()[1])[2];
    return "Mail sent.";
};

protected method .next_new_cmd() {
    arg cmdstr, cmd, str;
    var mail, list, keys, start;
    
    if (str) {
        catch any
            list = (> $mail_lib.match_mail_recipient(str) <);
        with
            return (traceback()[1])[2];
        .new_list(list);
        mail = (| list.mail_list_next((subscribed[list])[2]) |);
        while (mail && (mail.has_read(this()))) {
            refresh();
            mail = (| list.mail_list_next(mail) |);
        }
        if (!mail)
            return ("No new mail on " + ($mail_lib.mail_name(list))) + ".";
        .read_mail(mail, list);
    } else {
        keys = dict_keys(subscribed);
        if (!keys)
            return "You are not subscribed to any lists.";
        start = (list = current['list]);
        while (1) {
            // anything left on this list?
            mail = (| list.mail_list_next((subscribed[list])[2]) |);
            while (mail && (mail.has_read(this()))) {
                refresh();
                mail = (| list.mail_list_next(mail) |);
            }
            if (mail)
                break;
    
            // pick a new list
            catch any
                list = (> keys[(list in keys) + 1] <);
            with
                list = (| keys[1] |);
    
            // die?
            if ((!list) || (list == start))
                return "No new mail.";
        }
        .new_list(list);
        .read_mail(mail, list);
    }
};


new object $command_aliases: $user_interfaces;

var $root inited = 1;
var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'variables, 'core];
var $command_aliases command_aliases = [];
var $command_cache shortcut_cache = 0;
var $command_cache remote_cache = 0;
var $command_cache local_cache = 0;
var $root manager = $command_aliases;
var $root managed = [$command_aliases];
var $root owners = [$command_aliases];
var $root owned = [$command_aliases];

root method .init_command_aliases() {
    command_aliases = [];
};

root method .uninit_command_aliases() {
    command_aliases = [];
};

public method .command_aliases() {
    return command_aliases;
};

public method .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 method .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 = str.match_pattern(alias[1]);
        if (match != 0) {
            newstr = alias[2];
            for argf in [1 .. match.length()]
                newstr = newstr.replace("%" + tostr(argf), match[argf]);
            return newstr;
        }
    }
    return str;
};

public method .add_command_alias() {
    arg alias, actual;
    var relation, a;
    
    (> .perms(sender()) <);
    if ((type(alias) != 'string) || (type(actual) != 'string))
        throw(~type, "alias and actual are not strings.");
    relation = (> $command_lib.parse_relation(alias, actual) <);
    
    // 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, [(relation[1])[1], (relation[2])[2]]];
    
    // $# Edited 18 Oct 1995 12:55 Lynx ($lynx)
};

public method .del_command_alias() {
    arg alias;
    var ca, rel;
    
    (> .perms(sender()) <);
    if (type(alias) != 'string)
        throw(~type, "alias is not a string.");
    rel = (> $command_lib.parse_relation(alias, alias) <);
    rel = rel[1];
    for ca in (command_aliases) {
        if ((ca[1]) == (rel[1])) {
            command_aliases = command_aliases.setremove(ca);
            return;
        }
    }
    throw(~aliasnf, ("alias `" + alias) + "' is not found");
    
    // $# Edited 18 Oct 1995 13:19 Lynx ($lynx)
};


new object $bad_commands: $user_interfaces;

var $root inited = 1;
var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'variables, '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", "@status"], ["@alias", "@add-command-alias` or `@add-name-alias"], ["@check", "@monitor"], ["@paranoid", "@monitor"]];
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, []]]]]]], ["@alias", [["@alias", "*", "@alias <any>", 'old_command_cmd, #[[1, ['any, []]]]]]], ["@check|@paranoid", [["@check|@paranoid", "*", "@check|@paranoid <any>", 'old_command_cmd, #[[1, ['any, []]]]]]]];
var $command_cache shortcut_cache = 0;
var $command_cache remote_cache = 0;
var $command_cache local_cache = 0;
var $root manager = $bad_commands;
var $root managed = [$bad_commands];
var $root owners = [$bad_commands];
var $root owned = [$bad_commands];

public method .mangled_command() {
    arg str;
    
};

public method .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 method .del_old_cmd_reference();

public method .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 method .non_supported_cmds() {
    return non_supported_cmds;
};


new object $user_data: $user_interfaces, $has_settings;

var $root inited = 1;
var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'variables, '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 $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 = #[];
var $root manager = $user_data;
var $root managed = [$user_data];
var $root owners = [$user_data];
var $root owned = [$user_data];

public method .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 method .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 method .init_user_data() {
    user_data = #[['real_name, [1, "???"]], ['email, [1, "???"]]];
};

public method .valid_settings() {
    return valid_settings;
};

public method .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]).join();
    .set_data(what, value, (public["public"])[1]);
    
    // $#Edited: 18 Jul 96 14:56 $levi
};

public method .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 <variable> is|are <value>'");
        return 0;
    }
    out = ("Current personal data settings on " + (user.name())) + ":";
    
    //  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;
    
    // $# Edited 29 Oct 1995 13:48 Lynx ($lynx)
};

public method .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 method .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]).join();
    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)));
    
    // $#Edited: 18 Jul 96 14:56 $levi
};

public method .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;
    }
};


new object $help_root: $foundation;

var $root inited = 1;
var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'core, 'variables];
var $root manager = $help_root;
var $root managed = [$help_root];
var $root owners = [$help_root];
var $root owned = [$help_root];


new object $help_ui: $help_root, $user_interfaces;

var $root fertile = 1;
var $root inited = 1;
var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'fertile, 'variables, '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];
var $root manager = $help_ui;
var $root managed = [$help_ui];
var $root owners = [$help_ui];
var $root owned = [$help_ui];

protected method .help_indices() {
    return indices;
};

root method .init_help_ui() {
    history = [$help_lib.default_node()];
    indices = $help_lib.default_indices();
    current = 1;
    
    // $#Edited: 22 Jul 96 09:56 $jenner
    // $#Edited: 22 Jul 96 10:00 $jenner
};

public method .current_node() {
    return history[current];
};

protected method .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.join();
            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
                    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 {
            return (traceback()[1])[2];
        }
    }
    .set_help_node(node);
    .tell_help_node(node);
    
    // $#Edited: 18 Jul 96 14:56 $levi
};

protected method .last_visited() {
    (> .perms(sender()) <);
    return last_visited;
};

protected method ._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)().mmap('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 method ._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 method ._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 method .set_help_node() {
    arg node;
    
    if ((node in history) && ((history[current]) == node))
        return;
    while ((history.length()) >= ($help_lib.history_cap())) {
        history = history.delete(1);
        current--;
    }
    history = (history ? history.subrange(1, current) : []) + [node];
    current = history.length();
    
    // $#Edited: 01 Sep 96 16:33 $miro
};

protected method ._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 method ._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 {
            history = history.delete(node);
            .set_help_node(history[1]);
            .tell(line + ">> ERROR: INVALID NODE IN HISTORY <<");
        }
    }
};

protected method .tell_help_node() {
    arg node;
    var out, len, clen, line, n;
    
    len = ((.linelen()) % 2) ? (.linelen()) - 1 : (.linelen());
    .ptell(node.body(), #[['type, 'help], ['ctype, 'ctext]]);
    .tell((("--[" + (node.node_name())) + "]").pad(len, "-"));
    
    // $#Edited: 30 Jul 96 16:37 $jenner
};

root method .uninit_help_ui() {
    clear_var('history);
    clear_var('indices);
    clear_var('current);
};

protected method .help_node_history() {
    return history;
};


new object $messages_ui: $user_interfaces;

var $root fertile = 1;
var $root manager = $messages_ui;
var $root owners = [$messages_ui];
var $root inited = 1;
var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'fertile, 'variables, 'core];
var $has_commands local = #[["@mes?sages|@mesg|@msg", [["@mes?sages|@mesg|@msg", "*", "@mes?sages|@mesg|@msg <any>", 'messages_cmd, #[[1, ['any, []]]]]]]];
var $command_cache shortcut_cache = 0;
var $command_cache remote_cache = 0;
var $command_cache local_cache = 0;
var $root managed = [$messages_ui];
var $root owned = [$messages_ui];

protected method .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.name()) + " cannot have any messages.";
    if (!name)
        return .display_messages("", object);
    value = args.unquote();
    catch any {
        object.set_message(name, value);
        .tell("Message changed to:");
        return .display_messages(name, object);
    } with {
        return (traceback()[1])[2];
    }
    
    // $# Edited 05 Nov 1995 14:08 Lynx ($lynx)
    // $#Edited: 19 Aug 96 14:00 $jenner
};

public method .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];
            p = (obj.message(message)).uncompile();
            if (type(p) == 'dictionary) {
                for s in (p.keys()) {
                    if ($string.match_begin((message + ".") + s, mask))
                        output = [@output, [((("  " + message) + ".") + s) + " = "].affix(p[s])];
                }
            } else if ($string.match_begin(message, mask)) {
                output = [@output, [("  " + message) + " = "].affix(p)];
            }
        }
    }
    return output;
};


new object $settings_ui: $user_interfaces;

var $root inited = 1;
var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'variables, 'core];
var $has_commands local = #[["@set?tings", [["@set?tings", "on|from *", "@set?tings on|from <object reference>", 'settings_on_cmd, #[[2, ['objref, []]]]]]], ["@set|@setting?s", [["@set|@setting?s", "*", "@set|@setting?s <any>", 'set_cmd, #[[1, ['any, []]]]]]]];
var $command_cache shortcut_cache = 0;
var $command_cache remote_cache = 0;
var $command_cache local_cache = 0;
var $root manager = $settings_ui;
var $root managed = [$settings_ui];
var $root owners = [$settings_ui];
var $root owned = [$settings_ui];

public method .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 method .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 method ._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 method ._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 method .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 = args.match_regexp("<.*>");
    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 {
        return (traceback()[1])[2];
    }
    
    // $#Edited: 06 Aug 96 10:06 $jenner
};

public method ._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 == $has_settings)
            break;
        s = (| a.defined_settings() |);
        if (s)
            out += [._show_settings_on(a, object)];
    }
    return out.reverse();
};

public method ._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 {
        .tell((traceback()[1])[2]);
        throw(~stop, "", 'no_traceback);
    }
};


new object $editor_reference: $has_commands, $has_settings;

var $root manager = $editor_reference;
var $root flags = ['methods, 'code, 'fertile, 'core, 'variables];
var $root created_on = 820684615;
var $root owners = [$editor_reference];
var $root inited = 1;
var $editor_reference active_editor = 0;
var $has_commands shortcuts = #[];
var $has_commands remote = #[];
var $has_commands local = #[["@reedit", [["@reedit", "", "@reedit", 'reedit_cmd, #[]]]], ["@resume", [["@resume", "*", "@resume <any>", 'resume_cmd, #[[1, ['any, []]]]]]], ["@mcp-upload-session", [["@mcp-upload-session", "*", "@mcp-upload-session <descendant of $editor_session>", 'mcp_upload_cmd, #[[1, ['descendant, [$editor_session]]]]]]], ["@cleanup-sessions|@c-s?essions", [["@cleanup-sessions|@c-s?essions", "", "@cleanup-sessions|@c-s?essions", 'cleanup_sessions, #[]]]]];
var $editor_reference bg_sessions = 0;
var $has_settings defined_settings = #[["local-editor", #[['get, 'get_local_setting], ['set, 'set_local_setting], ['check, 'check_local_editor], ['del, 'delete_local_setting], ['check_args, []], ['get_args, []], ['set_args, []]]]];
var $has_settings local_settings = ["local-editor"];
var $has_settings settings = #[[$editor_reference, #[["local-editor", 'mcp]]]];
var $root managed = [$editor_reference];
var $root owned = [$editor_reference];

protected method .reinvoke_editor() {
    arg [session];
    var i, t;
    
    t = "";
    session = session ? session[1] : active_editor;
    if (session == 0)
        return "No session to resume.";
    if (active_editor && ((active_editor != session) || (!(| session.is_resumable() |))))
        t = (.store_editor()) + " ";
    if ((!valid(session)) || (!(session in (bg_sessions + [active_editor])))) {
        active_editor = 0;
        return "Invalid session - editor cleared.";
    }
    if (!(| session.is_resumable() |))
        return "The session is not resumable in the foreground.";
    active_editor = session;
    bg_sessions = bg_sessions.setremove(active_editor);
    bg_sessions = filter i in (bg_sessions) where (valid(i));
    .add_parser($editor_parser);
    return ((t + "Resumed ") + active_editor) + ".";
    
    // $#Edited: 21 Aug 96 15:44 $jenner
};

public method .invoke_editor() {
    arg [args];
    
    // finishing object, finishing method, initial text, client data;
    if (active_editor != 0)
        throw(~misc, "Editor already active");
    active_editor = $editor_session.spawn();
    if (active_editor.startup(@args))
        .add_parser($editor_parser);
    
    // $#Edited: 06 Aug 96 16:38 $jenner
};

public method .quit_editor() {
    if (active_editor == 0)
        throw(~misc, "No active editor.");
    .perms(sender(), active_editor);
    .del_parser($editor_parser);
    active_editor.destroy();
    active_editor = 0;
    
    // $#Edited: 06 Aug 96 16:38 $jenner
};

public method .store_editor() {
    var t;
    
    if (active_editor) {
        (sender() == this()) || (> .perms(sender(), active_editor) <);
        .del_parser($editor_parser);
        bg_sessions = (bg_sessions ? bg_sessions : []).setadd(active_editor);
        t = active_editor;
        active_editor = 0;
        return "Editor session %s saved.".format(t);
    }
    return "No active editor.";
    
    // $#Edited: 06 Aug 96 16:38 $jenner
};

protected method .reedit_cmd() {
    arg cmdstr, cmd, [session];
    var i;
    
    return .reinvoke_editor(@session);
    
    // $#Edited: 06 Aug 96 16:38 $jenner
};

public method .active_editor() {
    return active_editor;
    
    // $#Edited: 06 Aug 96 16:38 $jenner
};

public method .cleanup_sessions() {
    arg [who_cares];
    var i, out;
    
    (sender() == $housekeeper) || (> .perms(sender(), this()) <);
    out = [];
    for i in (bg_sessions || []) {
        if (valid(i) && (i.mcp_cleanup_session()))
            i.destroy();
        else
            out += [i];
    }
    bg_sessions = out;
    return "MCP and invalid editor sessions cleared.";
    
    // $#Edited: 18 Aug 96 21:59 $jenner
};

protected method .resume_cmd() {
    arg cmdstr, cmd, what;
    var i, session;
    
    if (what == "")
        session = bg_sessions ? [bg_sessions[1]] : [];
    else
        session = [$object_lib.to_dbref(what)];
    return .reinvoke_editor(@session);
    
    // $#Edited: 06 Aug 96 16:38 $jenner
};

public method .check_local_editor() {
    arg definer, value, [args];
    var styles;
    
    value = (| (styles = #[["None", 'none], ["MCP", 'mcp]])[value] |);
    if (!value)
        throw(~wrong, "Local editor must be one of: " + ((styles.keys()).to_english("", " or ")));
    return value;
    
    // $#Edited: 06 Aug 96 16:38 $jenner
};

public method .local_editor() {
    return (| .get_local_setting("local-editor", $editor_reference) |) || 'none;
    
    // $#Edited: 06 Aug 96 16:38 $jenner
};

public method .mcp_upload_cmd() {
    arg cmdstr, cmd, session;
    var text, status, tmp;
    
    text = (| .read("Reading the editor session text...") |);
    if (type(text) != 'list)
        return "Upload failed.";
    if (!(session in bg_sessions))
        return "Illegal session - upload failed.";
    tmp = active_editor;
    active_editor = session;
    catch any {
        status = session.mcp_upload(text);
    } with {
        .tell_traceback(traceback());
        status = "Compilation failed.";
    }
    active_editor = tmp;
    return status;
    
    // $#Edited: 21 Aug 96 15:26 $jenner
};

public method .edit_sessions_notify() {
    if (active_editor)
        .tell("<editor: Disconnected from an active session. Use @reedit to resume.>");
    if (bg_sessions)
        .tell(("<editor: You can @resume the following background sessions: " + (bg_sessions.to_english())) + ".>");
    
    // $#Edited: 17 Aug 96 19:37 $jenner
};

public method .do_save() {
    arg [info];
    
    // The purpose of this method is to ensure that save is ran with
    // this user's perms
    if ((!(sender().has_ancestor($editor_session))) || (!((sender().startup_sender()) == this())))
        throw(~perm, "Permission denied");
    return (> (info[1]).(info[2])(info[3], info[4]) <);
    
    // $#Edited: 18 Aug 96 19:07 $jenner
};


new object $channel_ui: $user_interfaces;

var $root manager = $channel_ui;
var $channel_ui active_channels = #[];
var $root flags = ['variables, 'methods, 'code, 'command_caches, 'command_cache, 'fertile, 'core];
var $root created_on = 838251646;
var $root owners = [$channel_ui];
var $channel_ui channel_dict = #[];
var $has_commands shortcuts = #[];
var $has_commands remote = #[];
var $has_commands local = #[["@addcom", [["@addcom", "*", "@addcom <any>", 'addcom_cmd, #[[1, ['any, []]]]]]], ["@channels", [["@channels", "*", "@channels <any>", 'list_channels_cmd, #[[1, ['any, []]]]]]], ["@delcom", [["@delcom", "*", "@delcom <any>", 'delcom_cmd, #[[1, ['any, []]]]]]]];
var $root inited = 1;
var $root managed = [$channel_ui];
var $root owned = [$channel_ui];

public method .broadcast() {
    arg channel, msg;
    var q, spammer_name, message;
    
    (> .perms(sender()) <);
    
    // is this really a command?. If so, do the command.
    switch (msg) {
        case "who":
            .channel_members(channel);
            return;
        case "off":
            .channel_off(channel);
            return;
        case "on":
            .channel_on(channel);
            return;
    }
    if (!(active_channels.contains(channel)))
        .tell("You must be on a channel to send a message to it.");
    
    // check for poses, thinking, etc.
    spammer_name = .name();
    switch (msg[1]) {
        case ":":
            message = (spammer_name + " ") + (msg.subrange(2));
        case "%":
            message = ((spammer_name + " . o O ( ") + (msg.subrange(2))) + " )";
        case "!":
            (> .channel_moderator_ok() <);
            message = msg.subrange(2);
        default:
            message = (spammer_name + ": ") + msg;
    }
    for q in ($user_db.connected())
        q.channel_msg(channel, message);
    
    // $#Edited: 24 Jul 96 18:09 $user_chuck
    // $#Edited: 22 Aug 96 20:27 $user_chuck
};

protected method .channel_on() {
    arg channel;
    
    if (!(active_channels.contains(channel))) {
        active_channels = active_channels.add(channel, 1);
        .broadcast(channel, ":has joined this channel.");
    } else {
        .tell("You are already on this channel.");
    }
    
    // $#Edited: 22 Aug 96 20:25 $user_chuck
};

public method .channel_moderator_ok() {
    (> .check_mojo() <);
    return 1;
    
    // $#Edited: 24 Jul 96 18:23 $user_chuck
};

protected method .channel_off() {
    arg channel;
    
    if (active_channels.contains(channel)) {
        .broadcast(channel, ":has left this channel.");
        active_channels = active_channels.del(channel);
    } else {
        .tell("You are not on this channel.");
    }
    
    // $#Edited: 22 Aug 96 20:26 $user_chuck
};

public method .list_channels_cmd() {
    arg cmdstr, cmd, [args];
    var cur_channels, i, active;
    
    cur_channels = channel_dict.keys();
    .tell("  Alias    Channel");
    for i in (cur_channels) {
        if (active_channels.contains(channel_dict[i]))
            active = "* ";
        else
            active = "  ";
        .tell(((active + (i.pad(8))) + " ") + (channel_dict[i]));
    }
    
    // $#Edited: 22 Aug 96 19:57 $user_chuck
};

public method .listen_channel() {
    arg channel;
    
    if (active_channels.contains(channel))
        return 1;
    return 0;
    
    // $#Edited: 22 Aug 96 19:55 $user_chuck
};

public method .channel_members() {
    arg channel;
    var q, members;
    
    members = [];
    for q in ($user_db.connected()) {
        if (q.listen_channel(channel))
            members = [@members, q.name()];
    }
    .tell(("Listening to channel " + channel) + ":");
    .tell(members.columnize(4));
    return members;
    
    // $#Edited: 24 Jul 96 18:49 $user_chuck
};

public method .channel_alias() {
    arg ch_alias;
    
    return (| channel_dict[ch_alias] |) || "";
    
    // $#Edited: 24 Jul 96 18:50 $user_chuck
};

public method .channel_msg() {
    arg channel, msg;
    
    (caller() == definer()) || (> .perms(sender(), 'system) <);
    if (active_channels.contains(channel))
        .tell((("<" + channel) + "> ") + msg);
    
    // $#Edited: 26 Aug 96 19:23 $user_chuck
    // $#Edited: 27 Aug 96 17:44 $user_chuck
};

protected method .init_channel_ui() {
    channel_dict = #[];
    active_channels = #[];
    
    // $#Edited: 26 Aug 96 19:38 $user_chuck
    // $#Edited: 27 Aug 96 17:45 $miro
};

protected method .addcom_cmd() {
    arg cmdstr, cmd, args;
    var elements;
    
    elements = args.explode("=");
    if ((elements.length()) == 2) {
        channel_dict = channel_dict.add(elements[1], (elements[2]).to_symbol());
        .tell((("Channel " + (elements[2])) + " added with alias ") + (elements[1]));
    } else {
        .tell("Syntax: @addcom <alias>=<channel_name>");
    }
    
    // $#Edited: 22 Aug 96 20:23 $user_chuck
};

protected method .delcom_cmd() {
    arg cmdstr, cmd, args;
    var elements;
    
    if (channel_dict.contains(args)) {
        channel_dict = channel_dict.del(args);
        .tell(("Channel alias " + args) + " deleted.");
    } else {
        .tell("You do not have that channel alias defined.");
    }
    
    // $#Edited: 22 Aug 96 20:20 $user_chuck
};


new object $user: $body, $interaction, $mail_ui, $command_aliases, $bad_commands, $user_data, $help_ui, $messages_ui, $has_messages, $settings_ui, $editor_reference, $channel_ui;

var $root owners = [$user];
var $root inited = 1;
var $root created_on = 796268969;
var $root owned = [$user];
var $root manager = $user;
var $root flags = ['methods, 'code, 'core, 'command_cache, 'variables];
var $root managed = [$user];
var $root child_index = 2;
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 $user task_connections = #[];
var $user global_tell = 0;
var $user formatter = $j_telnet_format;
var $user content_type = 'plain;
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, 0]]];
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, []]]]]]], ["@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 <descendant of $location>", 'get_from_cmd, #[[1, ['any, []]], [3, ['descendant, [$location]]]]]]], ["drop", [["drop", "*", "drop <thing>", 'drop_cmd, #[[1, ['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, []]]]]]], ["@chman?age", [["@chman?age", "*", "@chman?age <any>", 'chmanage_cmd, #[[1, ['any, []]]]]]], ["@manage?d", [["@manage?d", "*", "@manage?d <any>", 'managed_cmd, #[[1, ['any, []]]]]]], ["@remember", [["@remember", "* as *", "@remember <object> as <any>", 'remember_cmd, #[[1, ['object, []]], [3, ['any, []]]]]]], ["@remembered", [["@remembered", "*", "@remembered <any>", 'remembered_cmd, #[[1, ['any, []]]]]]], ["give|put", [["give|put", "* to|in *", "give|put <thing> to|in <thing>", 'give_to_cmd, #[[1, ['descendant, [$thing]]], [3, ['descendant, [$thing]]]]]]], ["discard", [["discard", "*", "discard <object>", 'discard_cmd, #[[1, ['object, []]]]]]], ["@owned", [["@owned", "*", "@owned <object>", 'owned_cmd, #[[1, ['object, []]]]]]], ["@writes", [["@writes", "*", "@writes <object>", 'writes_cmd, #[[1, ['object, []]]]]]], ["@trusted?-by", [["@trusted?-by", "*", "@trusted?-by <object>", 'trusted_by_cmd, #[[1, ['object, []]]]]]], ["@add-trust?ee|@at", [["@add-trust?ee|@at", "*", "@add-trust?ee|@at <object:>", 'add_trustee_cmd, #[[1, ['object_opt, []]]]]]], ["@del-trust?ee|@dt", [["@del-trust?ee|@dt", "*", "@del-trust?ee|@dt <object:>", 'del_trustee_cmd, #[[1, ['object_opt, []]]]]]], ["@monitor", [["@monitor", "*", "@monitor <any>", 'monitor_cmd, #[[1, ['any, []]]]]]], ["@ex?amine", [["@ex?amine", "*", "@ex?amine <object:+c?hop>", 'examine_cmd, #[[1, ['object_opt, [["c?hop"]]]]]]]], ["@map", [["@map", "", "@map", 'map_cmd, #[]]]]];
var $has_commands shortcuts = #[];
var $has_settings settings = #[[$user, #[["exit-style", 'cold]]]];
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]]], ["global-tell", #[['get, 'get_global_tell], ['set, 'set_global_tell], ['check, 'is_boolean], ['del, 'delete_local_setting], ['check_args, []], ['get_args, []], ['set_args, []], ['type, "boolean"], ['display, 'display_boolean_yes_no]]], ["use-cml", #[['get, 'get_local_setting_boolean], ['set, 'set_local_setting], ['check, 'is_any], ['del, 'delete_local_setting], ['check_args, []], ['get_args, []], ['set_args, []], ['type, "boolean"], ['display, 'display_boolean_yes_no]]], ["exit-style", #[['get, 'get_exit_style], ['set, 'set_local_setting], ['check, 'check_exit_style], ['del, 'delete_local_setting], ['check_args, []], ['get_args, []], ['set_args, []], ['display, 'display_exit_style]]]];
var $has_settings local_settings = ["exit-style"];
var $help_ui indices = [$help_index_root];
var $help_ui history = [$help_summary];
var $help_ui current = 1;
var $user info = 0;
var $user info_private = 0;
var $user tell_callers = 0;
var $user monitor = 0;
var $root trusted_by = [$user_db];
var $user failed = 0;
var $channel_ui channel_dict = #[];
var $channel_ui active_channels = #[];

root method .init_user() {
    password = "*";
    connected_at = 0;
    last_command_at = 0;
    connections = [];
    parsers = [$command_parser, $channel_parser];
    action = "";
    context = #[];
    .set_quota($sys.get_starting('quota));
    $user_db.insert(.name(), this());
    .set_flags([]);
    .move_to($body_cave);
    task_connections = #[];
    formatter = $j_telnet_format;
    info = #[];
    
    // $#Edited: 22 Aug 96 19:34 $jenner
};

root method .uninit_user() {
    var conn;
    
    if (.connected())
        (| (.location()).did_disconnect() |);
    
    // and incase any are lying about
    for conn in (connections)
        (| conn.interface_going_away() |);
    password = 0;
    connections = 0;
    (| $user_db.remove(.name()) |);
    
    // $#Edited: 08 Jan 96 09:25 Lynx ($lynx)
    // $#Edited: 10 Jul 96 01:41 $levi
};

public method .will_move() {
    arg mover, place;
    
    (> pass(mover, place) <);
    if (!(place.has_ancestor($place)))
        throw(~user, "Users can only move into descendants of $place.");
    if ((mover.is($user)) && ((mover != this()) && (!($sys.is_system(mover)))))
        throw(~user, "Only administrators can freely move users around.");
    
    // $#Edited: 28 Apr 96 15:02 $lynx
};

public method .connected_at() {
    return connected_at;
};

public method .last_command_at() {
    return last_command_at;
};

public method .tell() {
    arg what;
    
    if (!(.connected()))
        return;
    if (monitor != 0) {
        if (listlen(monitor) > 19)
            monitor = [[user(), @stack()[2], what], @delete(monitor, 10)];
        else
            monitor = [[user(), @stack()[2], what], @monitor];
    }
    ._tell(what);
};

public method .set_password() {
    arg str;
    
    (> .perms(sender(), 'manager) <);
    if ((str.length()) < 5)
        throw(~badpasswd, "Passwords must be at least 5 characters long.");
    password = crypt(str);
};

public method .check_password() {
    arg str, [cinfo];
    var match, warn;
    
    (> .perms(caller(), definer(), $login_interface) <);
    
    // no password means always match
    if (!password)
        return 1;
    
    // "*" means never match
    if (password == "*")
        return 0;
    match = match_crypted(password, str);
    
    // paranoia, i'm not sure how safe match_crypted() is --Brandon
    if (!match) {
        match = crypt(str, substr(password, 1, 2)) == password;
        if (match)
            $brandon.tell("OUCH: " + this());
    }
    
    // keep track of failed attempts, if its from a connection
    if ((!match) && cinfo) {
        if (.connected())
            .tell(("<Failed Login Attempt from " + (cinfo[1])) + ">");
        else
            failed++;
    }
    return match;
};

public method .did_move() {
    arg [args];
    
    (> pass(@args) <);
    (| .look_cmd() |);
};

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

public method .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 method .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 method .parse_line() {
    arg line;
    var parse, c, r, rval;
    
    (> .perms(caller(), $connection) <);
    set_user();
    
    // if we dont set it, it'll act like normal
    rval = this();
    last_command_at = time();
    catch any {
        task_connections = task_connections.add(task_id(), sender());
        parse = parsers ? (parsers[1]).parse(this(), line, @parsers.subrange(2), $null_parser) : ['failed];
        switch (parse[1]) {
            case 'error:
                .tell(parse[2]);
            case 'match, 'command:
                r = (> (parse[2]).(parse[3])(@parse.subrange(4)) <);
                if (type(r) in ['list, 'frob, 'string])
                    .ptell(r, #[['type, 'parser], ['command, parse[3]]]);
                else
                    rval = r;
            case 'failed:
                for c in (($places.coordinate_shortcuts()).keys()) {
                    if (line.match_template(c)) {
                        .tell(("There is no exit " + line) + " here.");
                        r = 1;
                    }
                }
                if (!r)
                    .tell(("I don't understand " + (line.chop((.linelen()) - 22))) + ".");
            case 'ok:
                // do nothing, probably a null command
            default:
                .tell("Unusual response from the parser: " + toliteral(parse));
        }
    } with {
        if (((traceback()[1])[3]) != 'no_traceback)
            .tell_traceback(traceback(), line, 0, error());
    }
    catch any
        task_connections = task_connections.del(task_id());
    return rval;
};

public method .login() {
    arg connection;
    var loc, cmd, p, c, last;
    
    if ((sender() != this()) || (definer() != caller()))
        throw(~perm, "Invalid access to private method.");
    task_connections = #[];
    (| .reset_parsers() |);
    for cmd in (.local_commands()) {
        for c in (cmd[2])
            .add_to_local_cache(c[1]);
    }
    for p in (parents())
        p.cache_init();
    $user_db.did_connect();
    last = connected_at;
    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)) |);
    } else {
        .look_cmd();
    }
    (| (.location()).did_connect() |);
    (| .login_notify(connection, last) |);
    (| .edit_sessions_notify() |);
    $channels.announce('login, (((.namef('titled)) + " has connected (total: ") + ($user_db.total_connected())) + ")", 0, [this()], this());
};

public method .login_again() {
    arg connection;
    
    if ((sender() != this()) || (definer() != caller()))
        throw(~perm, "Invalid access to private method.");
    last_command_at = time();
    (| .tell(("<login: " + (((.connections()).length()).n_to_nth())) + " login>") |);
    if (failed) {
        catch any {
            $brandon.tell("foo");
            (> .tell(("<login: ** " + failed) + " failed login attempts **>") <);
            (| clear_var('failed) |);
        } with {
            $brandon.tell_traceback();
        }
    }
};

public method .logout() {
    arg connection;
    var p;
    
    (| (.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() |);
    for p in (parents())
        p.cache_uninit();
    $channels.announce('login, (((.namef('titled)) + " has disconnected (total: ") + ($user_db.total_connected())) + ")", 0, [this()], this());
    task_connections = #[];
    .set_activity("");
    (| clear_var('monitor) |);
    (| .new_list(this()) |);
};

public method .connections() {
    return connections;
};

public method .connected() {
    return connections ? 1 : 0;
};

protected method .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 method .quit_cmd() {
    arg [args];
    
    return 'disconnect;
};

protected method .inventory_cmd() {
    arg [args];
    var i;
    
    if (.contents()) {
        .tell("Carrying:");
        for i in (.contents())
            .tell(" " + (i.name()));
    } else {
        .tell("You are empty-handed.");
    }
    
    // $# Edited 05 Nov 1995 13:54 Lynx ($lynx)
};

public method .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 {
        switch (error()) {
            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 method .linelen() {
    return linelen || 79;
};

public method .idle_seconds() {
    return time() - last_command_at;
};

public method .title() {
    return title || "";
};

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

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

protected method .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]).join();
    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)));
    
    // $#Edited: 18 Jul 96 14:56 $levi
};

public method .pagelen() {
    return pagelen || 24;
};

public method .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 method .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");
    if (len == 79)
        clear_var('linelen);
    else
        linelen = len;
    
    // $# Edited 22 Oct 1995 15:18 Lynx ($lynx)
};

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

public method .activity() {
    arg [noidle];
    var idle;
    
    // different from action, returns a broader version of your doings
    if (!(.connected()))
        return "asleep";
    if (activity)
        return activity;
    if (noidle)
        return "";
    idle = .idle_seconds();
    if (idle < 180)
        return "";
    if (idle < 300)
        return "daydreaming";
    if (idle < 900)
        return "zoned";
    else
        return "long gone";
    
    // $# Edited 28 Oct 1995 20:36 Lynx ($lynx)
};

protected method .add_name_alias_cmd() {
    arg cmdstr, cmd, args;
    var obj, alias;
    
    args = args.explode_quoted();
    if ((listlen(args) > 2) && ((args[2]) == "to"))
        args = delete(args, 2);
    if (listlen(args) > 2)
        args = [args[1], sublist(args, 2).join()];
    if ((!args) || (listlen(args) != 2))
        return ("Syntax: `" + cmd) + " \"<alias>\" [to] \"<object>\"`";
    obj = (> .match_env_nice(args[2]) <);
    alias = args[1];
    if (!(obj.is($named)))
        return obj + " cannot have regular names.";
    if (alias in (.name_aliases()))
        return (((obj.namef('ref)) + " already has the name alias \"") + alias) + "\"";
    catch any
        (> obj.add_name_alias(alias) <);
    with
        return (traceback()[1])[2];
    return ((((("Added name alias \"" + alias) + "\" to ") + (obj.namef('ref))) + ", aliases: ") + ((obj.name_aliases()).to_english())) + ".";
    
    // $#Edited: 18 Jul 96 14:56 $levi
};

protected method .del_name_alias_cmd() {
    arg cmdstr, cmd, args;
    var syn, obj, alias;
    
    args = args.explode_quoted();
    if ((listlen(args) > 2) && ((args[2]) == "from"))
        args = delete(args, 2);
    if (listlen(args) > 2)
        args = [args[1], sublist(args, 2).join()];
    if ((!args) || (listlen(args) != 2))
        return ("Syntax: `" + cmd) + " \"<alias>\" [to] \"<object>\"`";
    obj = (> .match_env_nice(args[2]) <);
    alias = args[1];
    if (!(obj.is($named)))
        return obj + " cannot have regular names.";
    if (!(alias in (.name_aliases())))
        return (((obj.name()) + " does not have the name alias \"") + alias) + "\"";
    catch any
        (> obj.del_name_alias(alias) <);
    with
        return (traceback()[1])[2];
    return ((((("Deleted name alias \"" + alias) + "\" from ") + (obj.namef('ref))) + ", aliases: ") + ((obj.name_aliases()).to_english("none"))) + ".";
    
    // $#Edited: 18 Jul 96 14:56 $levi
};

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

protected method .rename_cmd() {
    arg cmdstr, cmd, args;
    var obj, name, aliases, article, old, oldart, oldalias, old, new;
    
    args = args.explode_quoted();
    if ((listlen(args) > 2) && ((args[2]) == "to"))
        args = delete(args, 2);
    if (listlen(args) > 2)
        args = [args[1], sublist(args, 2).join()];
    if ((!args) || (listlen(args) != 2))
        return ("Syntax: `" + cmd) + " \"<object>\" [to] \"<newname>\" [+(proper|unique|normal)]`";
    obj = (> .match_env_nice(args[1]) <);
    catch any {
        name = (> $code_lib.parse_name(sublist(args, 2).join()) <);
        aliases = name[2];
        article = (name[1])[2];
        name = (name[1])[1];
        if ((name[1]) == "$") {
            if (((obj.is($user)) || (obj.is($place))) && (!(.is($admin))))
                return "User objnames can only be changed by administrators.";
            name = substr(name, 2);
            if (!(name.valid_ident()))
                return "Object names can only contain a-z, 0-9 and the underscore.";
            name = tosym(name);
            old = obj.namef('xref);
            (> obj.set_objname(name) <);
            return ((("Renamed " + old) + " to ") + obj) + ".";
        } else {
            if (!(obj.is($named)))
                return ("Object \"" + obj) + "\" cannot have a regular name.";
            old = ((((" [" + article) + "] \"") + (obj.name())) + "\"") + (aliases ? (" (" + ((obj.name_aliases()).to_english())) + ")" : "");
            oldart = (obj.name('literal))[1];
            old = ("\"" + (obj.name())) + "\"";
            (> obj.set_name(name, article) <);
            new = ("\"" + (obj.name())) + "\"";
            if (aliases) {
                (> obj.set_name_aliases(@aliases) <);
                if ((obj.name_aliases()) != oldalias) {
                    if (oldalias)
                        old += (" (" + (oldalias.to_english())) + ")";
                    new += (" (" + ((obj.name_aliases()).to_english())) + ")";
                }
            } else if (oldalias) {
                old += (" (" + (oldalias.to_english())) + ")";
            }
            if (oldart != ((obj.name('literal))[1])) {
                old = (("[" + oldart) + "] ") + old;
                new = (("[" + ((obj.name('literal))[1])) + "] ") + new;
            }
            return ((("Renamed " + old) + " to ") + new) + ".";
        }
    } with {
        return (traceback()[1])[2];
    }
    
    // $#Edited: 18 Jul 96 14:56 $levi
};

protected method .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]).join();
    full = (| "f?ull" in ((o[2]).slice(1)) |);
    if (full)
        full = ((o[2])[full])[3];
    owner = (| .match_environment(args) |);
    if (!owner) {
        owner = (| $user_db.search(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;
    
    // $#Edited: 18 Jul 96 14:56 $levi
};

protected method .login_notify() {
    arg connection, last;
    var l, sub, out;
    
    sub = .subscribed();
    out = [];
    for l in ((sub.keys()).setremove(this())) {
        if ((l.last_received_on()) > ((sub[l])[1]))
            out += [l.mail_name()];
    }
    if (out)
        .tell(("<mail: New mail on lists: " + (out.to_english())) + ">");
    if ((.last_received_on()) > (((.subscribed())[this()])[1]))
        .tell("<mail: You have new mail (use `@help mail` to learn about mail)>");
    .tell(("<login: Last connected at " + ($time.date(abs(last)))) + ">");
    if (failed) {
        (| .tell(("<login: ** " + failed) + " failed login attempts **>") |);
        (| clear_var('failed) |);
    }
};

protected method .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 method .add_command_alias_cmd() {
    arg cmdstr, cmd, input;
    
    input = input.explode_quoted();
    if (listlen(input) == 3)
        input = input.delete(2);
    if (listlen(input) != 2)
        return ("Syntax: `" + cmd) + " \"<alias>\" [to] \"<actual command>\"`";
    catch any
        (> .add_command_alias(@input) <);
    with
        return (traceback()[1])[2];
    return strfmt("New command alias %d => %d added.", @input);
};

protected method .del_command_alias_cmd() {
    arg cmdstr, com, template;
    
    template = explode(template, "\"")[1];
    catch ~aliasnf
        (> .del_command_alias(template) <);
    with
        return ("No command alias found matching \"" + template) + "\".";
    return ("Deleted command alias \"" + template) + "\".";
};

protected method .command_aliases_cmd() {
    arg cmdstr, com, what;
    var aliases, a, line, str, num, out, left, right;
    
    if (!what)
        what = this();
    else
        what = (> .match_env_nice(what) <);
    if ((what != this()) && (!(what.is_writable_by(this()))))
        return ("You are not allowed to read the command aliases on " + (what.namef('ref))) + ".";
    out = [("--- Command aliases on " + (what.namef('ref))) + ":"];
    aliases = what.command_aliases();
    if (aliases) {
        for a in (aliases) {
            str = a[1];
            num = 0;
            while ("*" in str) {
                num = num + 1;
                left = str.subrange(1, ("*" in str) - 1);
                right = str.subrange(("*" in str) + 1);
                str = ((left + "%") + tostr(num)) + right;
            }
            out = out + [strfmt("  %30d => %d", str, $command_lib.format_relation(a[2]))];
        }
    } else {
        out = out + ["  <none>"];
    }
    return out + ["---"];
};

protected method ._tell() {
    arg what, [args];
    var line, conn, type;
    
    switch (type(what)) {
        case 'frob:
            what = $parse_lib.filter_ctext(what, #[['formatter, formatter]]);
        case 'string:
            if (content_type == 'html)
                what = (((what.replace("&", "&amp;")).replace("<", "&lt;")).replace(">", "&gt;")) + "<br>";
        case 'list:
            for line in (what)
                ._tell(line);
            return;
        case 'buffer:
            throw(~nobuf, "You are not allowed to send buffers.");
    }
    conn = (| .task_connection() |);
    if (conn && (!global_tell)) {
        conn.write(what);
    } else {
        for conn in (connections)
            (| conn.write(what) |);
    }
};

public method .add_filter() {
    arg obj, [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.");
    if (!filters)
        filters = [];
    
    // does it exist?  If so remove it.
    if (obj in filters)
        filters = filters.setremove(obj);
    
    // figure position
    if (position == 'last)
        position = (filters.length()) + 1;
    else if (position == 'first)
        position = 1;
    filters = filters.insert(position, obj);
};

public method .del_filter() {
    arg obj;
    
    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(obj);
        if (!filters)
            (| clear_var('filters) |);
    } else {
        throw(~nofilters, "You do not have any tell filters.");
    }
};

protected method .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 method .filters() {
    .perms(sender());
    return filters;
};

public method .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.mmap(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 method .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 method .namef() {
    arg type;
    var str;
    
    switch (type) {
        case 'doing:
            str = .activity('noidle);
            if (str)
                return (((.name()) + " (") + str) + ")";
            return .name();
        case 'nactivity, 'activity:
            str = .activity();
            if (str)
                return (((.name()) + " (") + str) + ")";
            return .name();
        case 'titled:
            str = .title();
            if (str)
                return (((.name()) + " (") + str) + ")";
            return .name();
        default:
            return (> pass(type) <);
    }
    
    // $# Edited 28 Oct 1995 21:06 Lynx ($lynx)
};

protected method .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
        .tell_error(syn, (traceback()[1])[2]);
    .tell("Password changed.");
};

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

public method .match_context() {
    arg str;
    
    return context[str];
};

public method .context() {
    return context;
};

public method .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 method .non_terminated_tell() {
    arg text;
    var conn;
    
    if ((| .setting("terminated-tell") |)) {
        for conn in (.connections())
            conn.write(text, 'non_terminated);
    } else {
        .tell(text);
    }
};

public method .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 {
        (| pass(old_name, 'prop) |);
        rethrow(error());
    }
};

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

public method .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.search(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 method .age_cmd() {
    arg cmdstr, com, obj;
    var time, gender;
    
    time = obj.created_on();
    if (obj.is($gendered))
        gender = (obj.gender()).pronoun('psc);
    else
        gender = "It";
    .tell(((obj.name()) + " 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.");
    
    // $# Edited 05 Nov 1995 13:42 Lynx ($lynx)
};

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

protected method .description_cmd() {
    arg cmdstr, cmd, str;
    var args, obj, syn, desc, long;
    
    syn = cmd + " [<what> as <description>|<what>]";
    args = str.match_template("* as *");
    if (!args) {
        args = str.explode();
        if (!args)
            .tell_error(syn);
        obj = args.join();
        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 {
        .tell((traceback()[1])[2]);
    }
    
    // $#Edited: 18 Jul 96 14:56 $levi
};

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

public method .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 method .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 method .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 method .rehash_cmd() {
    arg cmdstr, cmd;
    var c, p;
    
    .tell("Rehashing your command caches...");
    pause();
    
    // purge first 
    (| .purge_caches() |);
    
    // let the parents know we are "going away"
    for p in (parents())
        p.cache_uninit();
    
    // now rehash, hand-pick our own commands
    for cmd in (.local_commands()) {
        for c in (cmd[2])
            .add_to_local_cache(c[1]);
    }
    
    // let the parents know we are back
    for p in (parents())
        p.cache_init();
};

public method .description() {
    arg flags;
    var c, contents, ctext, pronoun, lines, p, br;
    
    pronoun = (.gender()).pronoun('psc);
    if (connections)
        lines = [((pronoun + " is ") + ((.activity()) || "awake")) + "."];
    else
        lines = [(pronoun + " is asleep, and was last connected ") + ($time.format("%d %B %y %H:%M", abs(connected_at)))];
    if ((contents = .contents())) {
        lines = lines + [pronoun + " is holding:"];
        for c in (contents)
            lines = lines + ["  " + (c.name())];
    } else {
        lines = [lines, pronoun + " is holding nothing."];
    }
    return (> pass(flags) <) + lines;
};

protected method ._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.name())) + ".");
        return 0;
    }
    args = [who, "Users in " + (place.name())];
    args = [@args, [['namef, 'titled], ['idle_time]]];
    args = [@args, ["Name", "Times (idle)"], [1, 1]];
    return args;
};

protected method ._who_is() {
    arg [args];
    var person, p, who;
    
    who = [];
    args = ($string.explode_english_list(@args)) || [];
    for p in (args) {
        catch any {
            person = $user_db.search(p);
        } with {
            switch (error()) {
                case ~ambig:
                    .tell(((("The name \"" + p) + "\" can match any of: ") + ($list.to_english($list.mmap((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 method ._who_programmers() {
    arg args, all;
    var out, progs, p, x, t;
    
    progs = [];
    if (args && (!all)) {
        for p in (args) {
            x = $user_db.search(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 method ._who_admins() {
    arg args, all;
    var out, admins, a, x, t;
    
    admins = [];
    if (args && (!all)) {
        for a in (args) {
            x = $user_db.search(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 method ._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.name())) + " (") + ($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()));
    
    // $# Edited 05 Nov 1995 13:56 Lynx ($lynx)
};

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

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

protected method .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 method .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 method .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 method .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);
    
    // $#Edited: 27 Jan 96 16:51 Lynx ($lynx)
};

protected method .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.search(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()));
    }
};

public method .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 method .terminated_tell() {
    return (| (settings[$user])["terminated-tell"] |) || "text/plain";
};

public method .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)) + " (") + ((.parents())[1])) + "): ") + this()) + " <") + addr) + ">";
        (| $sys.log(line) |);
    } with {
        $sys.log($parse_lib.traceback(traceback()));
    }
};

public method .read() {
    arg [args];
    var text, output, head, tail;
    
    if (!(.connections()))
        return 'not_connected;
    if ((.task_connection()).is_reading_block())
        return 'engaged;
    if (args.length())
        head = args[1];
    else
        head = "Receiving input.  Enter \".\" to finish or \"@abort\" to abort.";
    if ((args.length()) > 1)
        tail = args[2];
    else
        tail = "** Aborted **; Text thrown away.";
    if (head)
        .tell(head);
    output = (.task_connection()).start_reading_block('multiple);
    if ((output == 'aborted) && tail)
        .tell(tail);
    return output;
};

public method .read_line() {
    arg [args];
    var line, abort_msg, head, con;
    
    if (!(.connections()))
        return 'not_connected;
    con = .task_connection();
    if (con.is_reading_block())
        return 'engaged;
    head = [@args, ""][1];
    abort_msg = [@args, "** Aborted **", "** Aborted **"][2];
    if (head)
        .tell(head);
    line = con.start_reading_block('one);
    if ((line == 'aborted) && abort_msg)
        .tell(abort_msg);
    return line[1];
    
    // $#Edited: 14 Jun 96 00:52 $levi
};

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

protected method .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 method .content_type() {
    return (| (settings_data[$user])["content-type"] |) || "text/plain";
};

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

protected method .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;
};

protected method .del_channel() {
    arg channel;
    
    if (!channels)
        return;
    channels = channels.del(channel);
    if (!channels)
        clear_var('channels);
    
    // $# Edited 16 Oct 1995 22:04 Lynx ($lynx)
};

protected method .add_channel() {
    arg channel, [value];
    
    if (!channels)
        channels = #[];
    if (!value)
        value = 1;
    channels = channels.add(channel, value);
    
    // $# Edited 16 Oct 1995 22:05 Lynx ($lynx)
};

protected method .listens_to_channel() {
    arg channel, [args];
    var cs;
    
    cs = .channels();
    if (args && ((channel == 'login) && (| type(cs['login]) == 'list |)))
        return (args[1]) in (cs['login]);
    return channel in (cs.keys());
    
    // $# Edited 16 Oct 1995 22:06 Lynx ($lynx)
};

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

public method .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 method .set_content_type() {
    arg definer, name, value, [args];
    var sets;
    
    (> .set_local_setting(definer, name, value, @args) <);
    if (value == "text/html") {
        content_type = 'html;
        formatter = $j_html_format;
    } else {
        content_type = 'plain;
        formatter = $j_telnet_format;
    }
    
    // $# Edited 20 Oct 1995 13:54 Lynx ($lynx)
};

public method .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 method .evaluator() {
    return evaluator;
};

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

protected method .get_cmd() {
    arg cmdstr, cmd, what;
    var ol, l;
    
    if ((what.location()) == sender()) {
        return ("You already have " + (what.name())) + ".";
    } 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((((.name()) + " takes ") + (what.name())) + ".", this(), what) |);
            (| l.announce((((.name()) + " takes ") + (what.name())) + ".", this(), what) |);
            return ("You take " + (what.name())) + ".";
        } with {
            if (error() == ~locked)
                return ((("You are unable to " + cmd) + " ") + (what.name())) + "!";
            else
                return (traceback()[1])[2];
        }
    }
    
    // $# Edited 05 Nov 1995 14:10 Lynx ($lynx)
};

protected method .drop_cmd() {
    arg cmdstr, cmd, what;
    
    if (!(what in (.contents()))) {
        return ("You don't have " + (what.name())) + ".";
    } else {
        catch any {
            (> what.move_to(.location()) <);
            .tell(("You drop " + (what.name())) + ".");
            (| (.location()).announce((((.name()) + " drops ") + (what.name())) + ".", this(), what) |);
        } with {
            return (traceback()[1])[2];
        }
    }
    
    // $# Edited 05 Nov 1995 14:10 Lynx ($lynx)
};

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

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

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

public method .is_tellable_by() {
    arg caller, sender;
    
    return (sender == this()) || (caller in [$place, $user, $programmer, $admin, $mail_ui, $help_ui, $messages_ui, $settings_ui, $channels]);
};

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

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

public method .connection_starting() {
    arg addr, port;
    var line, c;
    
    (> .perms(caller(), $connection) <);
    
    // shutoff our connection's timeout
    sender().set_timeout(0);
    
    // cleanup our connections list
    for c in (connections) {
        if (!valid(c))
            connections = connections.setremove(c);
    }
    connections = [sender()].union(connections);
    if ((connections.length()) == 1)
        (| .login(sender()) |);
    else
        (| .login_again(sender()) |);
    line = ("CONNECT " + tostr(sender() in connections)) + " (";
    line = (line + ((.parents())[1])) + "): ";
    line = (((line + this()) + " <") + (sender().address())) + "> ";
    (| $sys.log(line) |);
};

protected method .reset_parsers() {
    if (.command_aliases())
        parsers = [$command_aliases_parser, $command_parser, $channel_parser];
    else
        parsers = [$command_parser, $channel_parser];
    
    // $#Edited: 22 Aug 96 19:34 $jenner
};

public method .connected_seconds() {
    return connected_seconds;
};

protected method .add_trustee_cmd() {
    arg cmdstr, cmd, args;
    var syn, obj, trustee;
    
    trustee = args[1];
    args = args[2];
    if (args && ((args[1]) == "to"))
        args = delete(args, 1);
    obj = (> .match_env_nice(args.join()) <);
    catch any {
        (> obj.add_trusted(trustee) <);
        return [(("Added trustee " + (trustee.namef('ref))) + " to ") + (obj.namef('ref)), (((obj.namef('ref)) + " now trusts: ") + ((((obj.trusted('literal)).compress()).mmap('namef, 'ref)).to_english("nobody"))) + "."];
    } with {
        return (traceback()[1])[2];
    }
    
    // $#Edited: 18 Jul 96 14:56 $levi
};

protected method .del_trustee_cmd() {
    arg cmdstr, cmd, args;
    var syn, obj, trustee;
    
    trustee = args[1];
    args = args[2];
    if (args && ((args[1]) == "to"))
        args = delete(args, 1);
    obj = (> .match_env_nice(args.join()) <);
    catch any {
        (> obj.del_trusted(trustee) <);
        return [(("Removed trustee " + (trustee.namef('ref))) + " from ") + (obj.namef('ref)), (((obj.namef('ref)) + " now trusts: ") + ((((obj.trusted('literal)).compress()).mmap('namef, 'ref)).to_english("nobody"))) + "."];
    } with {
        .tell((traceback()[1])[2]);
    }
    
    // $#Edited: 18 Jul 96 14:56 $levi
};

protected method .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()).mmap('namef, 'xref)).to_english())) + "."];
    } with {
        .tell((traceback()[1])[2]);
    }
};

protected method .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()).mmap('namef, 'xref)).to_english())) + "."];
    } with {
        .tell((traceback()[1])[2]);
    }
};

protected method .writes_cmd() {
    arg cmdstr, cmd, what;
    
    return [(what.namef('ref)) + " is in the writers list for:"] + (._list_objects(what.writes(), 'writers));
};

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

protected method .owned_cmd() {
    arg cmdstr, cmd, what;
    
    return [(what.namef('ref)) + " owns:"] + (._list_objects(what.owned(), 'owners));
};

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

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

public method .remembered() {
    return remembered || #[];
};

protected method .get_from_cmd() {
    arg cmdstr, cmd, what, p, loc;
    var c, obj, l, wl, str;
    
    if (loc == this())
        return ("You already have " + (what.name())) + ".";
    for c in (loc.contents()) {
        if (c.match_name(what))
            obj = c;
    }
    if (!obj) {
        if (loc.is($place))
            return ("No \"" + what) + "\" in your environment.";
        else
            return ((loc.name()) + " does not have ") + what;
    }
    if ((obj == this()) || (obj == loc))
        return "Ewww, its all twisty.";
    if (loc.is($place))
        return .get_cmd(cmdstr, cmd, obj);
    catch any {
        l = .location();
        (> obj.move_to(this()) <);
        str = (((" " + (obj.name())) + " ") + p) + " ";
        wl = loc.location();
        .tell(((("You " + cmd) + str) + (loc.name())) + ".");
        loc.tell((((((.name()) + " ") + cmd) + "s") + str) + "you.");
        cmd = cmd + "s";
        str = (str + (loc.name())) + " ";
        if (l != wl)
            wl.announce((((.name()) + " ") + cmd) + str, this(), loc);
        l.announce((((.name()) + " ") + cmd) + str, this(), loc);
        return;
    } with {
        switch (error()) {
            case ~locked:
                if (loc.is($user))
                    loc.tell((((((.name()) + " tried to take ") + (obj.name())) + " ") + p) + " you.");
                return (((obj.name()) + " is locked to ") + ((obj.lock()).lock_name('thing))) + ".";
            default:
                return (traceback()[1])[2];
        }
    }
    
    // $# Edited 05 Nov 1995 13:58 Lynx ($lynx)
};

protected method .give_to_cmd() {
    arg cmdstr, cmd, what, p, loc;
    var c, obj, l, wl, str;
    
    if (!(what in (.contents())))
        return ("You don't have " + (what.name())) + ".";
    if (what == this())
        return "Give yourself away?  Interesting...";
    if (what == loc)
        return ((("Uhh, you cannot give " + ((what.gender()).pronoun('po))) + " to ") + ((what.gender()).pronoun('pr))) + ".";
    if (loc.is($place))
        return .drop_cmd(cmdstr, cmd, what);
    catch any {
        l = .location();
        wl = loc.location();
        (> what.move_to(loc) <);
        str = (((" " + (what.name())) + " ") + p) + " ";
        .tell(((("You " + cmd) + str) + (loc.name())) + ".");
        loc.tell((((((.name()) + " ") + cmd) + "s") + str) + "you.");
        cmd = cmd + "s";
        str = (str + (loc.name())) + " ";
        if (l != wl)
            wl.announce((((.name()) + " ") + cmd) + str, this(), loc);
        l.announce((((.name()) + " ") + cmd) + str, this(), loc);
    } with {
        .tell_traceback(traceback());
        return (traceback()[1])[2];
    }
};

public method .task_connections() {
    return task_connections;
};

public method .task_connection() {
    return task_connections[task_id()];
};

protected method .set_global_tell() {
    arg [args];
    
    if ((args[3]) == 0)
        clear_var('global_tell);
    else
        global_tell = 1;
    
    // $# Edited 20 Oct 1995 13:52 Lynx ($lynx)
};

public method .get_global_tell() {
    arg [args];
    
    return global_tell;
};

public method .channels() {
    return channels || #[['System, 1], ['login, 1]];
    
    // $# Edited 16 Oct 1995 22:05 Lynx ($lynx)
};

protected method .discard_cmd() {
    arg cmdstr, cmd, target;
    var msg, s;
    
    if ((cmdstr.trim()) == "discard") {
        s = .prompt(("Are you sure you want to discard " + (target.name())) + "? ");
        if (!(s in ["yes", "y"]))
            return ("Ok, not discarding " + (target.name())) + ".";
    }
    if ((target.location()) != this())
        return ("You are not holding " + (target.name())) + ".";
    if ((target.manager()) == this()) {
        target.destroy();
    } else {
        target.move_to($trash);
        msg = $mail_message.new_mail();
        msg.set_subject("Discarded Object");
        msg.set_text([(((((((target.name()) + " has been discarded by ") + (.name())) + ".  It currently exists in ") + ($trash.namef('ref))) + ", and will be destroyed in 15 days.  You may retrieve it with the command `@move ") + target) + " to me`."]);
        (| msg.send(target.manager()) |);
    }
    return ("Discarding " + (target.name())) + ".";
};

public method ._list_objects() {
    arg objs, multi, [args];
    var line, obj, c2, name, fmt, out, c1;
    
    if (!objs) {
        out = ["** None **"];
    } else {
        c2 = ((| sender().linelen() |) || 79) / 10;
        c1 = c2 * 4;
        fmt = "%3L%*L %*L %*L";
        out = [strfmt(fmt, "#", c1, "Name", c2, "Flags", c2, "Size") + "Manager"];
        for obj in (objs) {
            line = strfmt(fmt, obj.(multi)(@args).length(), c1, obj.namef('xref), c2, $object_lib.see_perms(obj, ["", ""]), c2, obj.size());
            name = (obj.manager()).namef('xref);
            if ((name.length()) > c1)
                name = name.pad(c1);
            out = out + [line + name];
        }
    }
    return out;
};

protected method .trusted_by_cmd() {
    arg cmdstr, cmd, what;
    
    return [(what.namef('ref)) + " is trusted by:"] + (._list_objects(what.trusted_by(), 'trusted));
};

protected method .personal_info() {
    return info || #[];
    
    // $#Edited: 01 May 96 17:31 $lynx
};

public method .personal_fields() {
    return #[["real-name", 1], ["email", 1], ["address", 1], ["affiliation", 1], ["position", 1], ["location", 1], ["interests", 1], ["plan", 1], ["projects", 1], ["home-page", 0]];
    
    // $#Edited: 01 May 96 17:33 $lynx
};

protected method .monitor_cmd() {
    arg cmdstr, cmd, [args];
    var e, out, line, len;
    
    if (!(args[1])) {
        if (monitor == 0)
            return "You are not monitoring what you hear.";
        out = [];
        len = .linelen();
        for e in (monitor || []) {
            line = strfmt("%20S %30S ", (e[1]).namef('xref), ((((e[2]) + ".") + (e[4])) + "() line ") + (e[5])) + (e[6]);
            if (strlen(line) > len)
                line = line.chop(len);
            out += [line];
        }
        return out + ["---"];
    }
    if ("on" in args) {
        monitor = [];
        return "You are now monitoring what you hear.";
    } else {
        (| clear_var('monitor) |);
        return "You are no longer monitoring what you hear.";
    }
    
    // $#Edited: 06 May 96 22:09 $lynx
};

protected method .monitor() {
    return monitor;
    
    // $#Edited: 06 May 96 21:51 $lynx
};

protected method .examine_cmd() {
    arg cmdstr, cmd, args;
    var obj, opts, i, chop, out, m, cmds, c;
    
    obj = args[1];
    opts = args[3];
    chop = .linelen();
    c = obj.created_on();
    if ((i = "ch?op" in (opts.slice(1))) && (!((opts[i])[3])))
        chop = 0;
    out = ["Object (@rename):    " + (obj.namef('ref)), "Created:             " + (c ? ctime(c) : "(Before Time)"), (("Quota:               " + ((obj.quota()).to_english())) + " bytes") + ((obj.quota_exempt()) ? " ** exempt **" : ""), ("Size:                " + ((obj.size()).to_english())) + " bytes (on disk)", "Perms (@chmod):      " + (((.flags()).prefix("+")).join()), "Manager (@chmanage): " + ($object_lib.get_name(obj.manager(), 'namef, ['ref])), ._exam_sub("Writer", 1, "@aw/@dw", chop, obj, 'writers, 'literal), ._exam_sub("Trusted", 0, "@at/@dt", chop, obj, 'trusted, 'literal), ._exam_sub("Owner", 1, "@ao/@do", chop, obj, 'owners), ._exam_sub("Parent", 1, "@ap/@dp", chop, obj, 'parents)];
    if (obj.has_ancestor($located))
        out += ["Location (@move):    " + ($object_lib.get_name(obj.location(), 'namef, ['xref]))];
    out += ["Description (@describe): ", @(type(obj.prose()) == 'frob) ? (obj.prose()).uncompile() : ((type(obj.prose()) == 'list) ? obj.prose() : [obj.prose()])];
    if ((cmds = obj.remote_commands()))
        out += $command_lib.format_commands_short(#[[obj, cmds]], "Remote", .linelen());
    if ((cmds = obj.local_commands()))
        out += $command_lib.format_commands_short(#[[obj, cmds]], "Local", .linelen());
    return out + ((| obj.examine() |) || []);
    
    // $#Edited: 18 Jul 96 14:56 $levi
    // $#Edited: 20 Jul 96 20:05 $jenner
};

protected method ._exam_sub() {
    arg name, plural, cmd, chop, obj, meth, [args];
    var list, line;
    
    list = (| obj.(meth)(@args) |) || [];
    if (!plural)
        line = pad(((name + " (") + cmd) + "):", 21) + (list.to_english("(none)"));
    else if (listlen(list) != 1)
        line = pad(((name + "s (") + cmd) + "):", 21) + (list.to_english("(none)"));
    else
        line = pad(((name + " (") + cmd) + "):", 21) + ((list[1]).namef('ref));
    if (chop)
        line = line.chop(chop);
    return line;
};

public method .check_exit_style() {
    arg definer, value, [args];
    var styles;
    
    // verify it is correct
    value = (| (styles = #[["Cold", 'cold], ["MOO", 'moo], ["Tiny", 'tiny]][value]) |);
    if (!value)
        throw(~wrong, "Style must be one of: " + ((styles.keys()).to_english("", " or ")));
    return value;
    
    // $#Edited: 06 Aug 96 10:09 $jenner
};

public method .get_exit_style() {
    arg name, definer, [args];
    
    return (| (settings[definer])[name] |) || ($user.get_local_setting(name, definer, @args));
};

public method .display_exit_style() {
    arg value;
    
    return value;
    
    // $#Edited: 08 Jul 96 15:15 $lynx
};

public method .map_cmd() {
    arg cmdstr, cmd;
    var l, obj, pos;
    
    l = .location();
    obj = (l.realm()).get_realm_var('map_object);
    pos = (l.realm()).get_realm_var('map_position);
    return (obj.has_ancestor($generic_map)) ? obj.view(@pos, 20, 79) : "This room doesn't have map defined for it.";
    
    // $#Edited: 25 Aug 96 19:31 $jenner
};


new object $guest: $user;

var $root child_index = 45;
var $root owners = [$guest];
var $root inited = 1;
var $root owned = [$guest];
var $root manager = $guest;
var $root quota = 75000;
var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'command_cache, 'variables, 'core];
var $root managed = [$guest];
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 formatter = #514;
var $user parsers = [$command_parser];
var $user tell_traceback = ['verbose, 4];
var $user context = #[];
var $user filters = [];
var $user task_connections = #[];
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, 0]]];
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 $user info = #[];
var $has_commands local = #[];
var $channel_ui channel_dict = #[];
var $channel_ui active_channels = #[];

root method .init_guest() {
    .set_title("a guest");
};

public method .logout() {
    arg connection;
    
    (| pass(connection) |);
    (> .destroy() <);
};

public method .title_cmd() {
    arg cmdstr, com, str;
    
    (> .perms(sender(), 'this) <);
    .tell("Guests are not allowed to change their titles.");
};

public method .login_notify() {
    arg connection, last;
    
    (| pass(connection, last) |);
    .tell(["**", "** Welcome to the Cold Dark.", "**", "** The Cold Dark is a development system for ColdC and the ColdCore.", "** There is no game involved in the Cold Dark, and in general the", "** discussions will range across various coding topics.  Guests and", "** ColdC questions are welcome and encouraged.  If you wish to access", "** the core you can do it through the web, or you can ask for programming", "** permissions.", "**"]);
    
    // $#Edited: 07 Jun 96 09:24 $lynx
};


new object $reaper: $user;

var $root manager = $reaper;
var $root quota = 75000;
var $root inited = 1;
var $root created_on = 796268969;
var $root flags = ['core];
var $root managed = [$reaper, $control];
var $has_settings local_settings = ["gender"];
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_male;
var $described prose = <$j_ctext_frob, [["A dark billowing cape covers his luminous and forboding form."], #[['this, #4]]]>;
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 formatter = #514;
var $user context = #[];
var $user task_connections = #[];
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, 0]]];
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;
var $user info = #[];
var $channel_ui channel_dict = #[];
var $channel_ui active_channels = #[];
var $root owned = [$reaper];
var $root owners = [$reaper];


new object $no_one: $user;

var $user task_connections = #[];
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 formatter = #514;
var $user parsers = [$command_parser];
var $user tell_traceback = ['brief, 0];
var $user context = #[];
var $user filters = [];
var $root inited = 1;
var $root manager = $no_one;
var $root quota = 75000;
var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'variables, 'core];
var $root managed = [$no_one];
var $command_aliases command_aliases = [];
var $location contents = [];
var $located location = $body_cave;
var $located obvious = 1;
var $mail_list letters = #[];
var $mail_list letters_index = #[];
var $mail_list senders = 1;
var $mail_list readers = [#2];
var $mail_list notify = [$no_one];
var $mail_list last_letter = 0;
var $mail_list mail = [];
var $mail_ui subscribed = #[[$no_one, [791485891, 0]]];
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;
var $user info = #[];
var $channel_ui channel_dict = #[];
var $channel_ui active_channels = #[];
var $root owners = [$no_one];
var $root owned = [$no_one];


new object $player: $user;

var $root child_index = 1;
var $root fertile = 1;
var $root manager = $player;
var $root created_on = 809926794;
var $root inited = 1;
var $root quota = 75000;
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 $user formatter = #514;
var $user task_connections = #[];
var $located location = $body_cave;
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, [813278562, 0]], [$mail_list_news, [813278562, 0]]];
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;
var $user info = #[];
var $channel_ui channel_dict = #[];
var $channel_ui active_channels = #[];
var $root flags = ['core];
var $root managed = [$player];
var $root owners = [$player];
var $root owned = [$player];

protected method .lower_attribute() {
    arg attribute, difference;
    var new;
    
    new = (get_var(attribute)[1]) - difference;
    set_var(attribute, get_var(attribute).replace(1, new));
};

public method .attribute(): nooverride, synchronized {
    arg attribute;
    
    if (caller() != definer())
        (> .perms(sender(), $storyteller) <);
    return get_var(attribute);
};

root method .init_player(): nooverride, 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 method .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));
    
    // 9-01-95/00:07 Lynx ($lynx), moved from $player.raise
};

public method .dead() {
    return dead;
};

public method .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 method .set_attribute() {
    arg attribute, value;
    
    (> .perms(sender()) <);
    set_var(attribute, [value, value]);
};


new object $storyteller: $player;

var $root manager = $storyteller;
var $located location = $body_cave;
var $root created_on = 809928922;
var $root inited = 1;
var $root flags = ['methods, 'code, 'variables, '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 = [];
var $user task_connections = #[];
var $user formatter = #514;
var $location contents = [];
var $user info = #[];
var $root managed = [$storyteller];
var $root owners = [$storyteller];
var $root owned = [$storyteller];


new object $realm_admin_ui: $user_interfaces;

var $root manager = $realm_admin_ui;
var $root flags = ['variables, 'methods, 'code, 'command_cache, 'core];
var $root created_on = 839898034;
var $root owners = [$realm_admin_ui];
var $has_commands shortcuts = #[];
var $has_commands remote = #[];
var $has_commands local = #[["@set-realm-var|@srv", [["@set-realm-var|@srv", "* to *", "@set-realm-var|@srv <any> to <any>", 'set_var_cmd, #[[1, ['any, []]], [3, ['any, []]]]]]], ["@list-r?ealm|@lrealm", [["@list-r?ealm|@lrealm", "*", "@list-r?ealm|@lrealm <object>", 'list_realm_cmd, #[[1, ['object, []]]]]]]];
var $root inited = 1;
var $root managed = [$realm_admin_ui];
var $root owned = [$realm_admin_ui];

protected method .set_var_cmd() {
    arg cmdstr, cmd, spec, onto, value;
    var holder, name, r;
    
    if (((spec = spec.explode(":")).length()) != 2)
        return "Var spec should be realm|room:variable.";
    if (!(holder = (| .match_env_nice(spec[1]) |)))
        return "Invalid realm.";
    name = tosym(spec[2]);
    if (holder.has_ancestor($realms_frob)) {
        catch any
            holder.set_realm_var(name, value ? holder.parse_variable(name, value) : 'unset);
        with
            return (traceback()[1])[2];
        return value ? "Variable %l set to %l.".format(name, holder.unparse_variable(0, name)) : ("Cleared %l.".format(name));
    } else if (holder.has_ancestor($place)) {
        r = holder.realm();
        catch any
            holder.set_realm(r.set_realm_var(name, value ? class(r).parse_variable(name, value) : 'unset));
        with
            return (traceback()[1])[2];
        return value ? "Variable %l set to %l.".format(name, (holder.realm()).unparse_variable(name)) : ("Cleared %l.".format(name));
    } else {
        return "Illegal value holder: %l.".format(holder);
    }
    
    // $#Edited: 24 Aug 96 18:58 $jenner
};

protected method .list_realm_cmd() {
    arg cmdstr, cmd, holder;
    var name, r;
    
    if (holder.has_ancestor($realms_frob)) {
        catch any
            return holder.all_realm_vars_text();
        with
            return (traceback()[1])[2];
    } else if (holder.has_ancestor($place)) {
        r = holder.realm();
        catch any
            return r.all_realm_vars_text();
        with
            return (traceback()[1])[2];
    } else {
        return "Illegal value holder: %l.".format(holder);
    }
    
    // $#Edited: 24 Aug 96 18:58 $jenner
};


new object $builder: $user, $realm_admin_ui;

var $root owners = [$builder];
var $root inited = 1;
var $root owned = [$builder];
var $root manager = $builder;
var $root quota = 75000;
var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'core, 'command_cache, 'variables];
var $root managed = [$builder];
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 formatter = #514;
var $user parsers = [$command_parser];
var $user tell_traceback = ['brief, 0];
var $user context = #[];
var $user filters = [];
var $user task_connections = #[];
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, 0]]];
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 = #[["@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, #[]]]], ["@mv|@move", [["@mv|@move", "*", "@mv|@move <object:>", 'move_cmd, #[[1, ['object_opt, []]]]]]], ["@child?ren|@kids", [["@child?ren|@kids", "*", "@child?ren|@kids <object>", 'children_cmd, #[[1, ['object, []]]]]]], ["@par?ents", [["@par?ents", "*", "@par?ents <object>", 'parents_cmd, #[[1, ['object, []]]]]]], ["@destroy|@dest", [["@destroy|@dest", "*", "@destroy|@dest <list object>", 'destroy_cmd, #[[1, ['list, ['object, []]]]]]]]];
var $help_ui indices = [$help_index_root];
var $help_ui history = [$help_summary];
var $help_ui current = 1;
var $user info = #[];
var $channel_ui channel_dict = #[];
var $channel_ui active_channels = #[];

protected method .children_cmd() {
    arg cmdstr, cmd, what;
    
    return [("Children of " + (what.namef('xref))) + ":"] + (._list_objects(what.children(), 'parents));
};

protected method .parents_cmd() {
    arg cmdstr, cmd, what;
    
    return [((("Parent" + ((((.parents()).length()) > 1) ? "s" : "")) + " of ") + (what.namef('ref))) + ":"] + (._list_objects(what.parents(), 'children));
};

protected method .destroy_cmd() {
    arg cmdstr, cmd, objs;
    var name, yes, obj;
    
    cmdstr = cmdstr.trim();
    if ((cmdstr.trim()) in ["@dest", "@destroy"]) {
        yes = .prompt(("Destroy '" + name) + "'? ");
        if (!(yes in ["y", "yes"]))
            return "Ok, aborting..";
    }
    for obj in (objs) {
        catch any {
            name = obj.namef('xref);
            (> obj.destroy() <);
            if (valid(obj))
                .tell(("Unable to destroy " + name) + " immediately.");
            else
                .tell(("Sucessfully destroyed " + name) + ".");
        } with {
            .tell((((cmd + " ") + obj) + ": ") + ((traceback()[1])[2]));
        }
    }
};

public method ._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 {
            .tell((traceback()[1])[2]);
            continue;
        }
        return [radial, azimuth];
    }
};

protected method .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 {
        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 method ._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 method ._build_hint() {
    arg n;
    
    (> .perms(sender()) <);
    if ((| .setting("experienced") |))
        return;
    .tell($places.build_hint(n));
    
    // $#Edited: 06 Jan 96 13:24 Lynx ($lynx)
};

public method ._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 method ._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 method .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 {
        .tell("Ack, unable to attach exit because:");
        .tell("  " + ((traceback()[1])[2]));
        (| exit.destroy() |);
        return;
    }
    .tell("Successfully attached exit.");
};

public method .realms_cmd() {
    arg cmdstr, cmd;
    var x, realms;
    
    (> .perms(sender(), 'this) <);
    realms = ($places.known_realms()).union($realms_frob.descendants());
    .tell("Realms: " + ((realms.mmap('name)).to_english()));
};

public method ._build_set_name() {
    arg obj, name, aliases;
    var a, x;
    
    (> .perms(sender(), 'this) <);
    catch any {
        obj.set_name(@name);
    } with {
        .tell("Unable to set name; " + ((traceback()[1])[2]));
        .tell("Setting name as " + tostr(obj.objname()));
        obj.set_name(tostr(obj.objname()));
    }
    for a in (aliases)
        obj.add_name_alias(a);
};

public method ._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 method ._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 {
        return [(> ._build_create_place(name) <), 1];
    }
};

public method ._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 method ._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 {
        ._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 method ._build_attach_exit() {
    arg source, dest, exit, coords;
    var line;
    
    (> .perms(sender()) <);
    catch any {
        exit.attach(source, dest, @coords);
    } with {
        .tell(("Unable to attach " + (exit.name())) + " because: ");
        .tell("  " + ((traceback()[1])[2]));
        line = .prompt("Continue building? ");
        if (line in ["no", "n"])
            throw(~abort, "Aborted");
    }
};

public method ._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 {
        if (eleave) {
            if (earrive)
                ._build_cleanup([eleave[1], 1], [earrive[1], 1]);
            else
                ._build_cleanup([eleave[1], 1]);
        }
        rethrow(error());
    }
    return [eleave, earrive];
};

public method .move_cmd() {
    arg cmdstr, cmd, args;
    var what, dest, loc, fromto;
    
    what = args[1];
    args = args[2];
    if (args && ((args[1]) == "to"))
        args = delete(args, 1);
    if (!args)
        return ("You have to move " + (what.namef('ref))) + " somewhere.";
    dest = (> .match_env_nice(args.join()) <);
    catch any {
        loc = what.location();
        fromto = ((" from " + ((| loc.name() |) || $nowhere)) + " to ") + (dest.name());
        (| what.tell(((("You are suddenly yanked" + fromto) + " by ") + (.name())) + ".") |);
        (> what.move_to(dest) <);
    
        // hook into messages eventually
        (| loc.announce((what.name()) + " suddenly disappears.") |);
        (| dest.announce((what.name()) + " suddenly appears.", what) |);
        return (("You move " + (what.name())) + fromto) + ".";
    } with {
        (| what.tell(("You feel as if " + (.name())) + " was incapable of moving you, you did not go anywhere.") |);
        return (traceback()[1])[2];
    }
    
    // $#Edited: 18 Jul 96 14:56 $levi
};


new object $help_editing_ui: $user_interfaces;

var $root fertile = 1;
var $root manager = $help_editing_ui;
var $root created_on = 805936681;
var $root inited = 1;
var $root flags = ['methods, 'code, 'fertile, 'variables, 'command_cache, '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, []]]]]]], ["@hnode|@help-spawn-node", [["@hnode|@help-spawn-node", "* from * index *", "@hnode|@help-spawn-node <any> from <descendant of $help_node> index <descendant of $help_index_root>", 'spawn_help_node, #[[1, ['any, []]], [3, ['descendant, [$help_node]]], [5, ['descendant, [$help_index_root]]]]]]]];
var $root managed = [$help_editing_ui];
var $root owners = [$help_editing_ui];
var $root owned = [$help_editing_ui];

protected method .help_write_cmd() {
    arg cmdstr, cmd, str;
    var node, text, errors;
    
    if (!str)
        node = .current_node();
    else
        node = $help_lib.parse_reference(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))) + ".";
    
    // $#Edited: 22 Jul 96 12:03 $jenner
};

protected method .help_list_cmd() {
    arg cmdstr, cmd, str;
    var node, out;
    
    if (!str)
        node = .current_node();
    else
        node = $help_lib.parse_reference(str);
    if (!(node.is($help_node)))
        return (node.namef('ref)) + " is not a descendant of $help_node.";
    return (["@hwrite " + node] + ((node.body()).uncompile())) + ["."];
    
    // $#Edited: 22 Jul 96 12:03 $jenner
};

protected method .spawn_help_node() {
    arg cmdstr, cmd, name, f, parent, ind, index;
    var newnode;
    
    (> (newnode = parent.spawn(name)) <);
    catch any {
        newnode.set_name(name, 'proper);
        newnode.set_body(["This node isn't written yet"]);
        newnode.add_index(index);
    } with {
        (| newnode.destroy() |);
        return (traceback()[1])[2];
    }
    return "Spawned node %l named '%l'.".format(newnode, newnode.name());
    
    // $#Edited: 22 Jul 96 11:59 $jenner
};


new object $programmer: $builder, $help_editing_ui;

var $root owners = [$programmer];
var $root inited = 1;
var $root owned = [$programmer];
var $root manager = $programmer;
var $root quota = 75000;
var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'core, 'command_cache, 'variables];
var $root managed = [$programmer];
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 formatter = #514;
var $user parsers = [$command_parser];
var $user tell_traceback = ['brief, 0];
var $user context = #[];
var $user filters = [];
var $user task_connections = #[];
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, 0]]];
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 = #[["@id", [["@id", "*", "@id <any>", 'id_cmd, #[[1, ['any, []]]]]]], ["@which", [["@which", "*", "@which <any>", 'which_cmd, #[[1, ['any, []]]]]]], ["@eval", [["@eval", "*", "@eval <any>", 'eval_cmd, #[[1, ['any, []]]]]]], ["@add-c?ommand|@ac", [["@add-c?ommand|@ac", "*", "@add-c?ommand|@ac <any>", 'add_command_cmd, #[[1, ['any, []]]]]]], ["@del-c?ommand|@dc", [["@del-c?ommand|@dc", "*", "@del-c?ommand|@dc <any>", 'del_command_cmd, #[[1, ['any, []]]]]]], ["@join", [["@join", "*", "@join <any>", 'join_cmd, #[[1, ['any, []]]]]]], ["@descend?ants", [["@descend?ants", "*", "@descend?ants <any>", 'descendants_cmd, #[[1, ['any, []]]]]]], ["@chpar?ents", [["@chpar?ents", "*", "@chpar?ents <any>", 'chparents_cmd, #[[1, ['any, []]]]]]], ["@chown", [["@chown", "*", "@chown <any>", 'chown_cmd, #[[1, ['any, []]]]]]], ["@teleport|@go", [["@teleport|@go", "*", "@teleport|@go <any>", 'teleport_cmd, #[[1, ['any, []]]]]]], ["@add-s?hortcut|@as", [["@add-s?hortcut|@as", "*", "@add-s?hortcut|@as <any>", 'add_shortcut_cmd, #[[1, ['any, []]]]]]], ["@del-m?ethod|@delm?ethod|@dm", [["@del-m?ethod|@delm?ethod|@dm", "*", "@del-m?ethod|@delm?ethod|@dm <objref>", 'del_method_cmd, #[[1, ['objref, []]]]]]], ["@rehash", [["@rehash", "", "@rehash", 'rehash_cmd, #[]]]], ["@trace-method|@trace", [["@trace-method|@trace", "*", "@trace-method|@trace <objref>", 'trace_method_cmd, #[[1, ['objref, []]]]]]], ["@ledit", [["@ledit", "*", "@ledit <objref: +e?dited>", 'local_edit_cmd, #[[1, ['objref_opt, [["e?dited"]]]]]]]], ["@d?isplay", [["@d?isplay", "*", "@d?isplay <objref: +c?hop +g?enerations=1 +d?efiners=1>", 'display_cmd, #[[1, ['objref_opt, [["c?hop"], ["g?enerations", "1"], ["d?efiners", "1"]]]]]]]], ["@program", [["@program", "*", "@program <objref: +w?arnings +e?dited=1 +a?ccess=1 +f?lags=1>", 'program_cmd, #[[1, ['objref_opt, [["w?arnings"], ["e?dited", "1"], ["a?ccess", "1"], ["f?lags", "1"]]]]]], ["@program", "* with *", "@program <objref: +w?arnings +e?dited=1> with <any>", 'program_cmd, #[[1, ['objref_opt, [["w?arnings"], ["e?dited", "1"]]]], [3, ['any, []]]]]]], ["@del-v?ariable|@dv", [["@del-v?ariable|@dv", "*", "@del-v?ariable|@dv <objref>", 'del_var_cmd, #[[1, ['objref, []]]]]]], ["@add-v?ariable|@av", [["@add-v?ariable|@av", "*", "@add-v?ariable|@av <objref:>", 'add_var_cmd, #[[1, ['objref_opt, []]]]]]], ["@show", [["@show", "*", "@show <objref: +c?hop>", 'show_cmd, #[[1, ['objref_opt, [["c?hop"]]]]]]]], ["@as", [["@as", "* eval *", "@as <object> eval <any>", 'eval_as_cmd, #[[1, ['object, []]], [3, ['any, []]]]]]], ["@def?iner", [["@def?iner", "* this|as * eval *", "@def?iner <object> this|as <object> eval <any>", 'eval_as_to_cmd, #[[1, ['object, []]], [3, ['object, []]], [5, ['any, []]]]]]], ["@add-owner|@ao", [["@add-owner|@ao", "*", "@add-owner|@ao <object:>", 'add_owner_cmd, #[[1, ['object_opt, []]]]]]], ["@mv|@move|@cp|@copy", [["@mv|@move|@cp|@copy", "*", "@mv|@move|@cp|@copy <objref:+c?omment=1>", 'move_cmd, #[[1, ['objref_opt, [["c?omment", "1"]]]]]]]], ["@del-s?hortcut|@ds", [["@del-s?hortcut|@ds", "*", "@del-s?hortcut|@ds <any>", 'del_shortcut_cmd, #[[1, ['any, []]]]]]], ["@add-p?arent|@ap", [["@add-p?arent|@ap", "*", "@add-p?arent|@ap <any>", 'add_parent_cmd, #[[1, ['any, []]]]]]], ["@del-p?arent|@dp", [["@del-p?arent|@dp", "*", "@del-p?arent|@dp <any>", 'del_parent_cmd, #[[1, ['any, []]]]]]], ["@grep", [["@grep", "*", "@grep <any:+d?escend +f?ull +l?ist +r?eplace-with=1>", 'grep_cmd, #[[1, ['any_opt, [["d?escend"], ["f?ull"], ["l?ist"], ["r?eplace-with", "1"]]]]]]]], ["@chmod|@mmod|@omod|@chflag?s", [["@chmod|@mmod|@omod|@chflag?s", "*", "@chmod|@mmod|@omod|@chflag?s <any>", 'chmod_cmd, #[[1, ['any, []]]]]]], ["@dump", [["@dump", "*", "@dump <any: +t?extdump +m?ethods +v?ariables +h?eader>", 'dump_cmd, #[[1, ['any_opt, [["t?extdump"], ["m?ethods"], ["v?ariables"], ["h?eader"]]]]]]]], ["@list", [["@list", "*", "@list <objref: +n?umbers +t?extdump>", 'list_cmd, #[[1, ['objref_opt, [["n?umbers"], ["t?extdump"]]]]]]]], ["@edit", [["@edit", "*", "@edit <objref: +t?ype=1>", 'edit_method_cmd, #[[1, ['objref_opt, [["t?ype", "1"]]]]]]]]];
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", <$j_ctext_frob, [["You teleport to ", <$j_generator, ["dest", [], [], 'gen_dest]>, "."], #[['this, #4]]]>], ["teleport.source", <$j_ctext_frob, [[<$j_generator, ["actor", [], [], 'gen_actor]>, " teleports to ", <$j_generator, ["dest", [], [], 'gen_dest]>, "."], #[['this, #4]]]>], ["teleport.dest", <$j_ctext_frob, [[<$j_generator, ["actor", [], [], 'gen_actor]>, " teleports here from ", <$j_generator, ["source", [], [], 'gen_source]>, "."], #[['this, #4]]]>]]]];
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_match_with], ['set, 'set_local_setting], ['check, 'check_match_with], ['del, 'delete_local_setting], ['check_args, []], ['get_args, []], ['set_args, []]]], ["match-default", #[['get, 'get_match_default], ['set, 'set_local_setting], ['check, 'is_any], ['del, 'delete_local_setting], ['check_args, []], ['get_args, []], ['set_args, []]]]];
var $has_settings local_settings = #[];
var $has_settings settings = #[];
var $user info = #[];
var $channel_ui channel_dict = #[];
var $channel_ui active_channels = #[];

root method .init_programmer() {
    .set_tell_traceback('verbose, 4);
};

protected method .eval_cmd() {
    arg cmdstr, com, str;
    var result, adjust, vars, v, evalp, times, line, reg;
    
    evalp = .eval_prefix();
    vars = (evalp.keys()).join(", ");
    v = (evalp.values()).join();
    
    // perform escape substitution
    if (str && ((str[1]) == "|"))
        str = substr(str, 2);
    else
        str = .eval_subs(str);
    if (!str) {
        result = (> .evaluate(((("var " + vars) + ";") + v) + "return (> 1 <);", 'no_offset) <);
        result = replace(result[1], 1, ((result[1])[1]) - 1);
        if (eval_offset)
            line = strfmt("adjusted by %s ticks and %s.%6{0}r seconds.", (eval_offset[1]) - (result[1]), (eval_offset[2]) - (result[2]), abs((eval_offset[3]) - (result[3])));
        else
            line = strfmt("set to %s ticks and %s.%6{0}r seconds.", @result);
        eval_offset = result;
        return "Eval offset " + line;
    }
    
    // clean it up
    str = strsed(str, "^;*", "");
    
    // format it, use heuristics
    if (match_begin(str, "var") && (reg = regexp(str, "var ([^;]+)"))) {
        str = strsed(str, "var ([^;]+);", "");
        str = ((((("var " + vars) + ", ") + (reg.join(","))) + ";") + v) + str;
    } else if ("return" in str) {
        str = ((("var " + vars) + ";") + v) + str;
    } else {
        str = strsed(str, " *;* *$", "");
        str = ((((("var " + vars) + ";") + v) + "return (> ") + str) + " <);";
    }
    result = (> .evaluate(str) <);
    times = result[1];
    result = result[2];
    
    // Display the errors, or the result.
    if ((result[1]) == 'errors) {
        .tell(result[2]);
    } else {
        if (type(result[2]) == 'objnum)
            .tell("=> " + ((| (result[2]).namef('xref) |) || (result[2])));
        else
            .tell("=> " + toliteral(result[2]));
        line = strfmt("[ ticks: %s; seconds: %s.%6{0}r", @times);
        if (times[2])
            line += (" (" + ((times[1]) / (times[2]))) + " ticks per second)";
        return line + " ]";
    }
};

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

protected method .show_cmd() {
    arg cmdstr, com, args;
    var show, match, i, chop, f, obj, out;
    
    if (((args[1])[1]) == 'object)
        show = ['method, 'variable];
    else if ((args[1])[5])
        show = [(args[1])[1], (args[1])[5]];
    else
        show = [(args[1])[1]];
    if ((i = "c?hop" in ((args[3]).slice(1))))
        chop = ((args[3])[i])[3];
    else
        chop = 1;
    if ((args[1])[4])
        f = (args[1])[4];
    else
        f = .setting("match-default");
    match = .setting("match-with");
    obj = (args[1])[3];
    .tell([((("Object:  " + obj) + " [") + ((obj.size()).to_english())) + " bytes]", "Parents: " + ((obj.parents()).join(", "))]);
    if ('method in show) {
        if (!(obj.has_flag('methods, this())))
            .tell("  ** No permission to list methods **");
        else
            .tell(._show_methods(obj, f, match, chop));
    }
    if ('variable in show) {
        if (!(obj.has_flag('variables, this())))
            .tell("  ** No permission to show variables **");
        else
            .tell(._show_variables(obj, f, match, chop));
    }
    
    // $#Edited: 18 Jul 96 14:56 $levi
};

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

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

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

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

protected method .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]) == 'variable)
            .tell("Variables are currently unsupported, sorry!");
        else
            $parse_lib.tell("You must specify a full reference for the source object.", syn);
    } with {
        $parse_lib.tell_error((traceback()[1])[2], syn);
    }
};

protected method .id_cmd() {
    arg cmdstr, cmd, obj;
    
    obj = .match_env_nice(obj);
    .tell((((((((obj.namef('xref)) + " ") + ($object_lib.see_perms(obj))) + " ") + toliteral(obj.parents())) + " ") + tostr(obj.size())) + " bytes");
};

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

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

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

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

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

protected method .eval_subs() {
    arg code;
    var idx, ret_code, sub;
    
    ret_code = "";
    while (code) {
        idx = "^" in code;
        if (!idx) {
            return ret_code + code;
        } else if ((idx == (code.length())) || ((code.subrange(idx + 1, 1)) == "^")) {
            ret_code = 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;
            code = code.subrange(idx);
        }
    }
    return ret_code;
};

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

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

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

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

protected method ._copy_move_method() {
    arg syn, how, ref1, ref2, opts;
    var line, code, type;
    
    .perms(sender(), 'this);
    catch any {
        code = (ref1[2]).list_method(ref1[3]);
        if (opts && ("-edited" in opts)) {
            if (!($sys.is_system(sender())))
                .tell("!  Only admins can turn off the edited messages.");
        } else {
            line = (" // " + ($time.ldate('date))) + "/";
            line = ((line + ($time.ltime('24hr))) + " ") + (.namef('ref));
            line = (((line + ", moved from ") + (ref1[2])) + ".") + tostr(ref1[3]);
            code = [@code, line];
        }
        if (how == 'move)
            (ref1[2]).del_method(ref1[3]);
        (ref2[2]).add_method(code, ref2[3]);
        line = ($string.capitalize(tostr(ref1[1]))) + " ";
        line = (((line + (ref1[2])) + ".") + tostr(ref1[3])) + " ";
        line = (line + ((how == 'move) ? "moved" : "copied")) + " ";
        line = (((line + (ref2[2])) + ".") + tostr(ref2[3])) + ".";
        .tell(line);
    } with {
        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());
        }
    }
    
    // $#Edited: 18 Sep 96 00:26 $admin_brad
};

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

protected method .eval_as_cmd() {
    arg cmdstr, cmd, obj, prep, line;
    var result;
    
    line = line.trim();
    if (!(";" in line))
        line = ("return " + line) + ";";
    result = obj.eval([line]);
    if ((result[1]) == 'errors)
        return result[2];
    else
        return (("eval as " + obj) + " => ") + toliteral(result[2]);
};

protected method .eval_as_to_cmd() {
    arg cmdstr, cmd, obj, this, target, eval, line;
    var result;
    
    line = line.trim();
    if (!(";" in line))
        line = ("return " + line) + ";";
    result = obj.eval([line], target);
    if ((result[1]) == 'errors)
        return result[2];
    else
        return (((((((cmd + " ") + obj) + " ") + this) + " ") + target) + " eval => ") + toliteral(result[2]);
};

protected method .teleport() {
    arg dest;
    var m, source, vars;
    
    (> .perms(sender(), 'this) <);
    source = .location();
    if (!(| .move_to(dest) |))
        return 0;
    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 method .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.search(who) <);
        }
    } with {
        .tell((traceback()[1])[2]);
        return;
    }
    loc = user.location();
    if (loc == (.location())) {
        .tell(("You are already with " + (user.name())) + "!");
        return;
    }
    if (!(.teleport(loc)))
        .tell("Sorry.");
    else
        .tell(("You join " + (user.name())) + ".");
};

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

protected method .display_cmd() {
    arg cmdstr, cmd, args;
    var opts, slice, what, match, i, chop, f, gen, def, obj, out;
    
    opts = args[3];
    args = args[1];
    chop = 1;
    slice = opts.slice(1);
    if ((i = "c?hop" in slice) && (!((opts[i])[3])))
        chop = 0;
    else
        chop = .linelen();
    def = args[3];
    if ((i = "g?enerations" in slice)) {
        gen = (opts[i])[4];
        if (gen.is_numeric())
            gen = ['generations, toint(gen)];
        else if (gen)
            gen = ['ancestors_to, (> .match_env_nice(gen) <)];
        else
            gen = ['ancestors_to, def];
        def = 0;
    } else {
        gen = ['generations, 1];
    }
    what = [args[1]] + ((| args[5] |) ? [args[5]] : []);
    obj = args[2];
    out = $object_lib.format_object(obj, chop);
    if (!(args[4]))
        f = .setting("match-default");
    else
        f = args[4];
    match = .setting("match-with");
    if ('method in what)
        out += ._display_methods(obj, obj.list_methods(gen, def, f, match), chop, f);
    if ('variable in what)
        out += ._display_variables(obj, obj.variable_info(gen, def, f, match), chop, f);
    return out + ["---"];
};

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

protected method .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) + " to ") + toliteral(owners)) + ".");
    } with {
        .tell((traceback()[1])[2]);
    }
    
    // $#Edited: 12 Jun 96 20:14 $levi
};

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

protected method .add_var_cmd() {
    arg cmdstr, cmd, args;
    var ref, value;
    
    ref = args[1];
    if (((ref[1]) != 'variable) || (!(ref[4])))
        return "Invalid obj,variable reference.";
    if ((args[2]) && (((args[2])[1]) == "=")) {
        args = ((args[2]).subrange(2)).join();
        value = .eval([("return " + args) + ";"]);
        if ((value[1]) == 'errors)
            return ("Unable to parse value \"" + args) + "\".";
        value = value[2];
    } else {
        value = 0;
    }
    catch ~symbol
        (ref[3]).add_var(tosym(ref[4]), value);
    with
        return (traceback()[1])[2];
    return ((((("Object variable " + (ref[3])) + ",") + (ref[4])) + " added with value ") + value) + ".";
    
    // $#Edited: 18 Jul 96 14:56 $levi
};

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

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

protected method .add_owner_cmd() {
    arg cmdstr, cmd, args;
    var syn, obj, owner;
    
    owner = args[1];
    args = args[2];
    if (args && ((args[1]) == "to"))
        args = delete(args, 1);
    obj = (> .match_env_nice(args.join()) <);
    catch any {
        obj.add_owner(owner);
        return [((("Added owner " + (owner.namef('ref))) + " to ") + (obj.namef('ref))) + ".", (((obj.namef('ref)) + " is now owned by: ") + ((obj.owners()).map_to_english('objname))) + "."];
    } with {
        return (traceback()[1])[2];
    }
    
    // $#Edited: 18 Jul 96 14:56 $levi
};

protected method .del_owner_cmd() {
    arg cmdstr, cmd, args;
    var obj, owner;
    
    owner = args[1];
    args = args[2];
    if (args && ((args[1]) == "to"))
        args = delete(args, 1);
    obj = (> .match_env_nice(args.join()) <);
    catch any {
        (> obj.del_owner(owner) <);
        return [((("Removed owner " + (owner.namef('ref))) + " from ") + (obj.namef('ref))) + ".", (((obj.namef('ref)) + " is now owned by: ") + ((obj.owners()).map_to_english('objname, "nobody"))) + "."];
    } with {
        return (traceback()[1])[2];
    }
    
    // $#Edited: 18 Jul 96 14:56 $levi
};

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

protected method .evaluate() {
    arg str, [no_offset];
    var start, end, time, ticks, mtime, times, method, errs;
    
    method = tosym("tmp_eval_" + time());
    if ((errs = .add_method([str], method)))
        return [[0, 0, 0], ['errors, errs, 0, 0]];
    catch any {
        times = [tick(), time(), mtime(), (> .(method)() <), mtime(), time(), tick()];
    } with {
        (| .del_method(method) |);
        rethrow(error());
    }
    (| .del_method(method) |);
    
    // figure up the actual times
    time = (times[6]) - (times[2]);
    ticks = (times[7]) - (times[1]);
    if ((times[5]) > (times[3]))
        mtime = (times[5]) - (times[3]);
    else if (time)
        mtime = ((time * 1000000) + (1000000 - (times[3]))) + (times[5]);
    else
        mtime = (1000000 - (times[5])) + (times[3]);
    
    // offset it?
    if (eval_offset && (!no_offset)) {
        ticks -= eval_offset[1];
        time -= eval_offset[2];
        mtime -= eval_offset[3];
    }
    return [[ticks, time, abs(mtime)], ['result, times[4]]];
};

protected method ._decompile() {
    arg obj, method, [args];
    var code, opt, flags, f;
    
    opt = [@args, "", ""][2];
    code = obj.list_method(method);
    flags = obj.method_flags(method);
    if (args && (args[1])) {
        code = code.numbered_text();
        return ([(((((("-- " + (obj.method_access(method))) + " method ") + obj) + ".") + method) + "()") + (flags ? ": " + (flags.join(", ")) : "")] + code) + ["--"];
    } else {
        return ([$object_lib.format_method_header(obj, method, opt, flags, obj.method_access(method))] + (code.prefix("  "))) + ["."];
    }
    
    // $#Edited: 18 Jul 96 14:56 $levi
};

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

public method .parse_methodcmd_options() {
    arg syntax, args, [more];
    var o, opt, opts, out, r, l;
    
    o = ([@more, []][1]) + [["pub?lic"], ["r?oot"], ["dr?iver"], ["pri?vate"], ["pro?tected"], ["no?override"], ["s?yncronized"], ["l?ocked"], ["na?tive"]];
    opts = #[['exists, 0], ['ignore, 0], ['mflags, []], ['mstate, 'public], ['error, 0]].union([@more, #[], #[]][2]);
    args = $parse_lib.getopt(args, o);
    if (!(args[1])) {
        out = [];
        for opt in (o)
            out = [@out, "  +|-" + (opt[1])];
        (> .tell_error(syntax, ["Valid options:"] + (out.lcolumnize())) <);
    }
    r = (| $parse_lib.ref((args[1]).join()) |);
    if (!r) {
        opts = opts.add('error, "Invalid <object>.<method> reference.");
        opts = opts.add('ignore, 1);
    }
    if (!((r[4]).valid_ident())) {
        opts = opts.add('error, (((r[2]) + ".") + tostr(r[4])) + "() is not a valid method reference.");
        opts = opts.add('ignore, 1);
    }
    r = replace(r, 4, tosym(r[4]));
    if ((r[2]) && (!((r[2]).is_writable_by(this())))) {
        opts = opts.add('error, ("You cannot program " + (r[2])) + ".");
        opts = opts.add('ignore, 1);
    }
    if ((| (r[2]).find_method(r[4]) |) == (r[2])) {
        opts = opts.add('mflags, (r[2]).method_flags(r[4]));
        opts = opts.add('mstate, (r[2]).method_access(r[4]));
        opts = opts.add('exists, 1);
    }
    opts = opts.add('object, r[2]);
    opts = opts.add('method, r[4]);
    for opt in (args[2]) {
        switch (opt[1]) {
            case "pub?lic", "r?oot", "dr?iver", "pri?vate", "pro?tected":
                opts = opts.add('mstate, ((opt[1]).strip("?")).to_symbol());
            case "no?override", "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;
    
    // $#Edited: 08 Jan 96 09:29 Lynx ($lynx)
    // $#Edited: 18 Jul 96 14:56 $levi
};

protected method .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
        return (traceback()[1])[2];
    return ((("Manager on " + (obj.namef('xref))) + " changed to ") + (manager.namef('xref))) + ".";
};

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

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

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

protected method .edit_method_cmd() {
    arg cmdstr, com, args;
    var def, s, code, ref, type, status, p;
    
    s = "@edit <object reference> [+type=...]";
    ref = args[1];
    type = (| ((args[3])[1])[4] |) || "any";
    switch (ref[1]) {
        case 'variable:
            (> .tell_error("", "Variable editor not yet implemented.") <);
        case 'method:
            def = (| (ref[2]).find_method(tosym(ref[4])) |);
            if (!def)
                (> .tell_error(s, "Method not found.") <);
            code = .format_method(def, tosym(ref[4]), 'normal);
            (> .invoke_editor(this(), '_edit_method_callback, code.subrange(2, (code.length()) - 2), [def, tosym(ref[4])]) <);
            return "Editor invoked.";
        case 'object:
            if (!(| (p = (ref[2]).all_edit_types()) |))
                return "The object is not editable.";
            if (type == "any")
                type = p[1];
            if (!(| (ref[2]).(tosym("edit_" + type))() |))
                return ((("Could not edit " + (ref[2])) + "'s ") + type) + ".";
            return "Editor invoked.";
    }
    
    // $#Edited: 18 Aug 96 21:10 $jenner
    // $#Edited: 22 Aug 96 20:26 $jenner
};

protected method ._edit_method_callback() {
    arg code, client_data;
    var errors;
    
    errors = (client_data[1]).add_method(code, client_data[2]);
    if (errors)
        return errors;
    return "Method compiled.";
    
    // $#Edited: 18 Aug 96 20:07 $jenner
};

public method .get_match_default() {
    arg name, definer, [args];
    
    catch ~setting
        return (> .get_local_setting(name, definer, @args) <);
    with
        return "*";
};

public method .get_match_with() {
    arg name, definer, [args];
    
    catch ~setting
        return (> .get_local_setting(name, definer, @args) <);
    with
        return 'match_pattern;
};

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

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

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

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

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

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

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

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

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

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

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

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

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

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


new object $system_object: $core;

var $root manager = $system_object;
var $root created_on = 796540258;
var $root inited = 1;
var $root flags = ['methods, 'code, 'core, 'variables];
var $root managed = [$system_object];
var $root owners = [$system_object];
var $root owned = [$system_object];

root method .uninit_system_object() {
    $sys.sender_going_away();
};


new object $utilities: $core;

var $root child_index = 14;
var $root inited = 1;
var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'core, 'variables];
var $root manager = $utilities;
var $root managed = [$utilities];
var $root owners = [$utilities];
var $root owned = [$utilities];


new object $world: $utilities;

var $root fertile = 1;
var $root inited = 1;
var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'fertile, 'variables, 'core];
var $world starting_place = 0;
var $root manager = $world;
var $root managed = [$world];
var $root owners = [$world];
var $root owned = [$world];

public method .daylight() {
    // should eventually return something like 'day 'night 'dusk 'dawn
    return 'day;
};

public method .starting_place() {
    return starting_place || $body_cave;
};


new object $frob: $core;

var $root child_index = 10;
var $root fertile = 1;
var $root inited = 1;
var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'fertile, 'core, 'variables];
var $root manager = $frob;
var $root managed = [$frob];
var $root owners = [$frob];
var $root owned = [$frob];

public method .unparse() {
    arg rep;
    
    return ((("<" + this()) + ", ") + rep) + ">";
    
    // $#Edited: 13 Jun 96 01:14 $levi
};

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

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

public method .value() {
    arg value;
    
    return value;
};


new object $network: $foundation;

var $root child_index = 3;
var $root manager = $network;
var $root created_on = 809051864;
var $root inited = 1;
var $root flags = ['methods, 'code, 'core, 'variables];
var $root managed = [$network];
var $root owners = [$network];
var $root owned = [$network];

public method .hostname(): native;

public method .ip(): native;


new object $connection_interface: $network, $command_cache, $frob;

var $root child_index = 2;
var $root manager = $connection_interface;
var $root created_on = 809051864;
var $root inited = 1;
var $root quota_exempt = 1;
var $root flags = ['methods, 'code, 'core, 'variables];
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;
var $root managed = [$connection_interface];
var $root owners = [$connection_interface];
var $root owned = [$connection_interface];

public method .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)).join()) || ""];
            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 {
        if (((traceback()[1])[3]) != 'no_traceback) {
            (this['connection]).write($parse_lib.traceback(traceback()));
            return 'disconnect;
        }
    }
    
    // $#Edited: 18 Jul 96 14:56 $levi
};

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

public method .linelen() {
    arg [args];
    
    return 79;
};

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

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

public method .null_cmd() {
    arg [args];
    
    return 'disconnect;
};

public method .invalid_cmd() {
    arg [args];
    
    return 'disconnect;
};

public method .connection() {
    return connection;
};

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

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

public method .new_connection() {
    arg this, host, port;
    
};

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

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


new object $login_interface: $connection_interface;

var $root child_index = 8216;
var $root manager = $login_interface;
var $root created_on = 809051864;
var $root inited = 1;
var $root quota_exempt = 1;
var $root flags = ['methods, 'code, 'core, 'variables];
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, #[]]]], ["cr?eate", [["cr?eate", "*", "cr?eate <any>", 'create_cmd, #[[1, ['any, []]]]]]]];
var $command_cache shortcut_cache = [];
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"]], ["cr", ["cr?eate"]], ["cre", ["cr?eate"]], ["crea", ["cr?eate"]], ["creat", ["cr?eate"]], ["create", ["cr?eate"]]];
var $root managed = [$login_interface];
var $root owned = [$login_interface];
var $root owners = [$login_interface];

protected method .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)).join();
    user = (| $user_db.search(name) |) || (.tell_error(syn, stderr));
    (user.check_password(passwd, (.connection()).address())) || (.tell_error(syn, stderr));
    (.connection()).change_interface(user);
};

protected method .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 {
        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 method .quit_cmd() {
    arg cmdstr, cmd;
    
    .print("Goodbye.");
    return 'disconnect;
};

protected method .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();
    c = .connection();
    if ((args.length()) < 2)
        (> .tell_error(syn) <);
    name = (args.subrange(1, (args.length()) - 1)).join();
    email = args[args.length()];
    if ($sys.validate_email_addresses())
        result = $code_lib.valid_email(email);
    if ((result[1]) != 'valid) {
        switch (result[1]) {
            case 'invalid:
                c.write(["", ("=> Syntax: `" + 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 {
        c.write(["", ("=> Syntax: `" + syn) + "`"]);
        c.write((traceback()[1])[2]);
        c.write("If there is a problem contact: " + semail);
        return;
    }
    c.change_interface(user);
    
    // $#Edited: 18 Jul 96 14:56 $levi
};

protected method .who_cmd() {
    arg cmdstr, cmd;
    
    .print($code_lib.generate_listing($user_db.connected()));
};

protected method .help_cmd() {
    arg cmdstr, cmd;
    
    .print($motd.connect_help());
};

protected method .null_cmd() {
    arg [args];
    
    return (> .invalid_cmd(@args) <);
};

protected method .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 method .print() {
    arg what;
    
    (.connection()).write(what);
};

protected method .motd() {
    var out;
    
    out = ($motd.build('default)) + ["", " ** Use 'H?elp' for a list of commands **".center(79), ""];
    return out;
};

protected method .motd_cmd() {
    arg cmdstr, cmd;
    
    .print(.motd());
};

protected method .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 method .connection_starting() {
    arg host, port;
    
    (> .perms(caller(), $connection) <);
    .print(.motd());
};

public method .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)).join()) || ""];
            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(cmd[2], (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 {
        if (((traceback()[1])[3]) != 'no_traceback) {
            .print($parse_lib.traceback(traceback()));
            return 'disconnect;
        }
    }
    
    // $#Edited: 18 Jul 96 14:56 $levi
};

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

public method .coreify_login_interface() {
    (> .perms(caller(), $sys) <);
    .add_command("cr?eate <any>", 'create_cmd);
    .rehash_caches();
    
    // $#Edited: 22 Sep 96 09:49 $user_brandon
};


new object $http_interface: $connection_interface;

var $root child_index = 6207;
var $root manager = $http_interface;
var $root managed = [$http_interface];
var $root created_on = 809075102;
var $root inited = 1;
var $root quota_exempt = 1;
var $root flags = ['methods, 'code, 'core, 'variables];
var $root owners = [$http_interface];
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;
var $root owned = [$http_interface];

public method .new() {
    arg c;
    var i;
    
    (> .perms(caller(), $connection) <);
    i = .pull_from_cache();
    i.set_connection(c);
    return i;
};

public method .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 method .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 method .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 method .init_http_interface() {
    .initialize_http();
};

public method .close() {
    var c;
    
    if (connection) {
        .log_request(connection.address());
        c = connection;
        connection = 0;
        c.close();
        definer().add_obj_to_cache(this());
    }
};

public method .connection_starting() {
    arg addr, port;
    
};

public method .connection_going_away() {
    arg addr, port;
    
    (| .close() |);
    .initialize_http();
    definer().add_obj_to_cache(this());
};

public method .initialize_http() {
    if (caller() != definer())
        throw(~perm, "Caller is not " + definer());
    method = "";
    http = "HTTP/1.0";
    status = 200;
    URI = "";
    bytes = 0;
    ctype = $http_lib.html_version();
    full = 1;
    header = #[];
};

public method .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 method .respond() {
    arg body;
    
    if (type(body) != 'buffer)
        body = $buffer.from_strings(body);
    bytes = body.length();
    .send_header();
    connection.write(body);
    .close();
};

public method .respond_with_file() {
    arg fstat;
    
    bytes = fstat[2];
    .send_header();
    connection.cwritef("html" + URI);
    .close();
};

public method .find_filename() {
    var file, actual, stat, ifiles, i;
    
    stat = (| fstat("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 = (| fstat("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 method .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 method .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 method .handle_method() {
    if (!method) {
        status = 400;
        return .respond($http_lib.response(status, "No Method specified!"));
    }
    .(tosym("http_method_" + method))();
};

public method .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 method .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"]).join());
    $sys.log(line);
    
    // $#Edited: 18 Jul 96 14:56 $levi
};

public method .http_method_GET() {
    var info, target, gate;
    
    if (URI == "/") {
        .respond($motd.build_html());
    } else if ($string.match_begin(URI, "/~")) {
        target = (| $user_db.search(((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 {
                status = 500;
                return .respond($http_lib.html_traceback(500, traceback()));
            }
        }
    } else if ((info = .find_filename())) {
        .respond_with_file(info);
    }
};

public method .http_method_HEAD() {
    var stat;
    
    if (!(stat = .find_filename()))
        return;
    bytes = stat[2];
    .send_header();
    .respond_with_file(stat);
    .close();
};

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

public method .set_cached() {
    arg v;
    
    if (caller() != definer())
        throw(~perm, ("Caller is not " + definer()) + ".");
    cached = v;
};

public method .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();
};


new object $connection: $network;

var $root child_index = 7;
var $root manager = $connection;
var $root created_on = 809051864;
var $root inited = 1;
var $root flags = ['methods, 'code, 'core, 'variables];
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;
var $connection foreign_addr = 0;
var $connection local_port = 0;
var $connection foreign_socket = 0;
var $connection local_addr = 0;
var $connection remote_port = 0;
var $connection remote_addr = 0;
var $connection remote_name = 0;
var $root managed = [$connection];
var $root owners = [$connection];
var $root owned = [$connection];

public method .daemon_shutdown() {
    var c;
    
    // called by $daemon.stop_listening()
    (> .perms(caller(), $daemon) <);
    for c in (.children())
        (| c.close() |);
    
    // $#Edited: 06 Aug 96 19:45 $brandon
};

root method .init_connection() {
    buffer = `[];
    local_addr = (remote_addr = "");
    line_buffer = [];
    timeout = 300;
    
    // remove all flags
    .set_flags([]);
    
    // $#Edited: 06 Aug 96 19:45 $brandon
};

root method .uninit_connection() {
    (| close_connection() |);
    active = 0;
    if (interface)
        (| interface.connection_going_away(.address(), remote_port) |);
    interface = 0;
    
    // $#Edited: 06 Aug 96 19:45 $brandon
};

public method .timeout() {
    return timeout;
    
    // $#Edited: 06 Aug 96 19:45 $brandon
};

public method .address() {
    (> .perms(sender()) <);
    return remote_addr;
    
    // if you want, remove the above return and you will exec the following
    // note: this currently blocks everything while doing name lookups
    if (!remote_name)
        remote_name = .hostname(remote_addr);
    return remote_name;
    
    // $#Edited: 06 Aug 96 19:45 $brandon
};

public method .set_timeout() {
    arg seconds;
    
    (> .perms(sender(), interface) <);
    timeout = seconds;
    if (!timeout)
        (| clear_var('timeout) |);
    
    // $#Edited: 06 Aug 96 19:45 $brandon
};

public method .change_interface() {
    arg new;
    var old;
    
    (> .perms(sender()) <);
    if (interface) {
        old = interface;
        old.connection_going_away(.address(), remote_port);
        .del_writer((| class(old) |) || old);
    }
    interface = new;
    .add_writer((| class(new) |) || new);
    interface.connection_starting(.address(), remote_port);
    
    // $#Edited: 06 Aug 96 19:45 $brandon
};

public method .new_interface() {
    arg obj;
    
    (> .perms(sender()) <);
    interface = obj;
    
    // $#Edited: 06 Aug 96 19:45 $brandon
};

public method .set_daemon() {
    arg obj;
    
    (> .perms(sender()) <);
    daemon = obj;
    
    // $#Edited: 06 Aug 96 19:45 $brandon
};

public method .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.");
    }
    cwrite(what);
    
    // $#Edited: 06 Aug 96 19:45 $brandon
};

driver method .parse() {
    arg incoming;
    var lines, line, index;
    
    lines = (buffer + 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 method .parse_line() {
    arg line;
    
    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 method .cwritef() {
    arg fname;
    
    (> .perms(sender()) <);
    (> cwritef(fname) <);
};

public method .is_reading_block() {
    return read_block ? 1 : 0;
};

public method .finish_reading_block() {
    var task_id, lines;
    
    (> .perms(sender()) <);
    task_id = read_block.task_id();
    lines = read_block.lines();
    read_block = 0;
    $scheduler.resume(task_id, lines);
};

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

public method .start_reading_block() {
    arg count;
    
    (> .perms(sender()) <);
    read_block = $read_parser.new(task_id(), count);
    return (> $scheduler.suspend(this()) <);
};

public method .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 method .do_timeout() {
    (> .perms(sender(), $scheduler) <);
    if (!timeout)
        return;
    .write(("Timeout (" + tostr(timeout)) + ")");
    .close();
    
    // $#Edited: 06 Aug 96 19:45 $brandon
};

public method .close() {
    (> .perms(sender()) <);
    (> .destroy() <);
};

driver method .disconnect() {
    .close();
};

public method .set_remote_port() {
    arg port;
    
    (> .perms(sender()) <);
    remote_port = port;
    
    // $#Edited: 06 Aug 96 19:45 $brandon
};

public method .new_connection() {
    var new, i;
    
    (> .perms(caller(), $daemon) <);
    new = .spawn();
    i = interface.new(new);
    new.add_writer(sender());
    new.add_writer(interface);
    new.add_writer((| class(i) |) || i);
    new.add_writer(this());
    new.new_interface(i);
    new.change_manager(new);
    return new;
    
    // $#Edited: 06 Aug 96 20:12 $brandon
};

public method .active() {
    return active;
};

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

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

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

public method .active_since() {
    return active;
};

public method .start() {
    arg remote, local, rport, lport;
    
    // Make this method 'fork' from the regular thread stack
    (> .perms(caller(), $daemon) <);
    active = time();
    remote_addr = remote;
    remote_port = rport;
    local_addr = local;
    local_port = lport;
    if (timeout)
        $scheduler.add_task(timeout, 'do_timeout);
    interface.connection_starting(.address(), remote_port);
};

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

public method .interface_going_away() {
    if ((sender() != interface) && (sender() != (| class(interface) |)))
        throw(~perm, sender() + " is not the interface.");
    interface = 0;
    (> .destroy() <);
    
    // $#Edited: 10 Jul 96 01:40 $levi
};


new object $login_connection: $connection;

var $root child_index = 9321;
var $root manager = $login_connection;
var $root created_on = 809051865;
var $root inited = 1;
var $root quota_exempt = 1;
var $root owners = [$login_connection];
var $root flags = ['methods, 'code, 'core, 'variables];
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;
var $root managed = [$login_connection];
var $root owned = [$login_connection];

public method .local_echo_off() {
    (sender() == (.interface())) || (> .perms(sender()) <);
    .write_buffer(`[255, 251, 1, 0]);
    
    // $# Edited 18 Oct 1995 23:48 Lynx ($lynx)
};

public method .local_echo_on() {
    (sender() == (.interface())) || (> .perms(sender()) <);
    .write_buffer(`[255, 252, 1, 0]);
    
    // $# Edited 18 Oct 1995 23:49 Lynx ($lynx)
};


new object $http_connection: $connection;

var $root child_index = 6219;
var $root manager = $http_connection;
var $root created_on = 809075134;
var $root inited = 1;
var $root quota_exempt = 1;
var $root flags = ['methods, 'code, 'core, 'variables];
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;
var $root managed = [$http_connection];
var $root owners = [$http_connection];
var $root owned = [$http_connection];

public method .address() {
    return (> pass() <);
};


new object $daemon: $network;

var $root child_index = 4;
var $root manager = $daemon;
var $root created_on = 809051864;
var $root inited = 1;
var $root flags = ['methods, 'code, 'core, 'variables];
var $daemon default_port = 0;
var $daemon connection = 0;
var $daemon next_connection = 0;
var $daemon current_port = 0;
var $root managed = [$daemon];
var $root owners = [$daemon];
var $root owned = [$daemon];

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

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

public method .stop_listening() {
    arg [port];
    
    (> .perms(sender()) <);
    catch any
        (> connection.daemon_shutdown() <);
    with
        $brandon.tell_traceback(traceback());
    next_connection = 0;
};

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

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

public method .current_port() {
    return current_port;
};

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

driver method .connect() {
    arg remote, local, socket;
    var conn;
    
    catch any {
        if (!valid(next_connection))
            next_connection = connection.new_connection();
        conn = next_connection;
        reassign_connection(conn);
        next_connection = connection.new_connection();
        conn.start(remote, local, socket, current_port);
    } with {
        $brandon.tell_traceback(traceback());
    }
};


new object $http_daemon: $daemon;

var $root manager = $http_daemon;
var $root created_on = 809075222;
var $root inited = 1;
var $root flags = ['methods, 'code, 'core, 'variables];
var $daemon default_port = 1180;
var $daemon connection = $http_connection;
var $daemon next_connection = #995;
var $daemon current_port = 1180;
var $root managed = [$http_daemon];
var $root owners = [$http_daemon];
var $root owned = [$http_daemon];


new object $login_daemon: $daemon;

var $root manager = $login_daemon;
var $root created_on = 809051992;
var $root inited = 1;
var $root flags = ['methods, 'code, 'core, 'variables];
var $daemon connection = $login_connection;
var $daemon default_port = 1138;
var $daemon current_port = 1138;
var $daemon next_connection = #997;
var $root managed = [$login_daemon];
var $root owners = [$login_daemon];
var $root owned = [$login_daemon];


new object $veil_daemon: $daemon;

var $root manager = $veil_daemon;
var $root flags = ['methods, 'code, 'variables, 'core];
var $root created_on = 814668214;
var $root owners = [$veil_daemon];
var $root inited = 1;
var $daemon default_port = 1100;
var $daemon connection = $veil_connection;
var $root managed = [$veil_daemon];
var $root owned = [$veil_daemon];


new object $veil_connection: $network;

var $root manager = $veil_connection;
var $root inited = 1;
var $root created_on = 814651762;
var $root child_index = 2;
var $veil_connection last_id = 0;
var $veil_connection ch_types = 0;
var $veil_connection channels = 0;
var $veil_connection incomplete = 0;
var $veil_connection host = 0;
var $veil_connection packets = 0;
var $veil_connection ch_info = 0;
var $veil_connection daemon = 0;
var $veil_connection active = 0;
var $veil_connection timeout = 0;
var $root flags = ['methods, 'code, 'variables, 'core];
var $root managed = [$veil_connection];
var $root owners = [$veil_connection];
var $root owned = [$veil_connection];

driver method .parse() {
    arg incoming;
    var p, message, i;
    
    // p = buf_to_veil_packets(incomplete, incoming);
    incomplete = p.last();
    i = 1;
    message = `[];
    while (packets) {
        p = packets[i];
        message += p[4];
        if (p[1]) {
            packets = packets.subrange(i + 1);
            (| .message(p[1], p[2], message) |);
            message = `[];
            i = 1;
        }
        i = i + 1;
    }
    
    // using subscripting keeps us from copying the list of packets every
    // iteration of the while loop.  We want to do it in the above way,
    // as then the packet list and current message is stored on the object,
    // which saves us if the current task suspends.
    // $# Edited 25 Oct 1995 18:41 Lynx ($lynx)
};

public method .write() {
    arg buf, id, info, push;
    
    // cwrite(buf_from_veil_packets([[push, info, ch, buf]]));
    // $# Edited 25 Oct 1995 18:41 Lynx ($lynx)
};

public method .next_id() {
    last_id = last_id + 2;
    return last_id;
    
    // $# Edited 25 Oct 1995 18:41 Lynx ($lynx)
};

protected method .packet() {
    arg packet;
    var control;
    
    control = (| channels[packet[3]] |);
    if (!control) {
        control = .new_channel(@packet.subrange(1, 3));
    
        // just ignore the packet
        if (!control)
            return;
    }
    control.incoming(packet);
    
    // $# Edited 25 Oct 1995 17:51 Lynx ($lynx)
};

protected method .new_channel() {
    arg id, buf;
    var lines, reg, par;
    
    // parse the buffer to figure out what type of channel it is.
    // for now everything is 'shell;
    par = ((buf[1]) * 256) + (buf[2]);
    lines = subbuf(buf, 3).to_strings();
    channels = channels.add(id, $user_rascal);
    ch_info = ch_info.add(id, [par, lines]);
    
    // $# Edited 25 Oct 1995 18:41 Lynx ($lynx)
};

protected method .message() {
    arg status, id, buf;
    var control;
    
    control = (| channels[id] |);
    switch (status) {
        case 'abort:
            .close_channel(id);
            return;
        case 'close:
            .close_channel(id);
        case 'open:
            if (control)
                return;
            control = .new_channel(id, buf);
            return;
    }
    if (!control)
        return;
    
    // fork this
    control.inbound(buf);
    
    // $# Edited 25 Oct 1995 18:45 Lynx ($lynx)
};

protected method .close_channel() {
    arg id;
    
    channels = (| channels.del(id) |);
    ch_info = (| ch_info.del(id) |);
    
    // $# Edited 25 Oct 1995 18:41 Lynx ($lynx)
};

public method .outbound() {
    arg what, type, [nopush];
    var id, a;
    
    id = (| ch_types[type] |);
    if (id == ~keynf)
        throw(~invch, strfmt("Invalid channel type: %d.", type));
    if (!id) {
        id = .new_id();
        ch_types = ch_types.add(type, id);
        a = 'open;
    }
    switch (type(what)) {
        case 'string:
            what = $buffer.from_strings([what], `[10]);
        case 'list:
            what = $buffer.from_strings(what, `[10]);
        case 'buffer:
        default:
            throw(~type, "Write: strings, list of strings and buffers.");
    }
    
    // cwrite(buf_from_veil_packets([[nopush ? 0 | 1, a, id, buf]]));
    // $# Edited 25 Oct 1995 18:41 Lynx ($lynx)
};

public method .init_veil_connection() {
    incomplete = `[];
    packets = [];
    channels = #[];
    ch_types = #[];
    ch_info = #[];
    last_id = 1;
    host = "";
    daemon = 0;
    active = 0;
    timeout = 0;
    .set_flags([]);
    
    // $# Edited 25 Oct 1995 18:41 Lynx ($lynx)
};

public method .close() {
    arg [msg];
    var c;
    
    (caller() == definer()) || (> .perms(sender()) <);
    msg = $buffer.from_strings([@msg, ["Goodbye"]][1]);
    active = 0;
    for c in (channels.keys())
        .write(msg, ch, 'close, 1);
    (| close_connection() |);
    for c in (channels)
        (| (c[2]).connection_going_away() |);
    channels = #[];
    
    // $# Edited 25 Oct 1995 18:41 Lynx ($lynx)
};

root method .uninit_connection() {
    if (active)
        .close(["** Connection aborted due to object destruction **"]);
    
    // $# Edited 25 Oct 1995 18:41 Lynx ($lynx)
};

public method .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;
    
    // $# Edited 25 Oct 1995 18:41 Lynx ($lynx)
};

public method .daemon_shutdown() {
    var c;
    
    (> .perms(caller(), $daemon) <);
    for c in (.children())
        (> .close(["** Connection aborted due to daemon shutdown **"]) <);
    
    // $# Edited 25 Oct 1995 18:41 Lynx ($lynx)
};

public method .timeout() {
    return timeout;
    
    // $# Edited 25 Oct 1995 18:41 Lynx ($lynx)
};

public method .address() {
    (> .perms(sender()) <);
    return host;
    
    // $# Edited 25 Oct 1995 18:41 Lynx ($lynx)
};

public method .set_host() {
    arg host;
    
    (> .perms(sender()) <);
    set_var('host, host);
    
    // $# Edited 25 Oct 1995 18:41 Lynx ($lynx)
};

public method .set_daemon() {
    arg daemon;
    
    (> .perms(sender()) <);
    set_var('daemon, daemon);
    
    // $# Edited 25 Oct 1995 18:41 Lynx ($lynx)
};

protected method .do_timeout() {
    arg seconds;
    
    .close([("VEIL: ** Timeout " + tostr(seconds)) + " seconds **"]);
    
    // $# Edited 25 Oct 1995 18:41 Lynx ($lynx)
};

driver method .disconnect() {
    arg [args];
    var c;
    
    // this is called by the driver, after the connection is dead, so
    // we have no connection already.
    active = 0;
    for c in (channels)
        (| (c[2]).connection_going_away() |);
    channels = #[];
    
    // $# Edited 25 Oct 1995 18:41 Lynx ($lynx)
};

public method .set_port() {
    arg port;
    
    (> .perms(sender()) <);
    set_var('port, port);
    
    // $# Edited 25 Oct 1995 18:41 Lynx ($lynx)
};

public method .active() {
    return active;
    
    // $# Edited 25 Oct 1995 18:41 Lynx ($lynx)
};

driver method .connect() {
    arg client, server, socket;
    
    host = client;
    daemon.new_connection();
};

public method .active_since() {
    return active;
    
    // $# Edited 25 Oct 1995 18:41 Lynx ($lynx)
};

public method .start() {
    // Make this method 'fork' from the regular thread stack
    (> .perms(caller(), $daemon) <);
    active = time();
    
    // do something
    // $# Edited 25 Oct 1995 18:41 Lynx ($lynx)
};


new object $event_frob: $frob;

var $root inited = 1;
var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'variables, 'core];
var $event_frob type = 'generic;
var $root manager = $event_frob;
var $root managed = [$event_frob];
var $root owners = [$event_frob];
var $root owned = [$event_frob];

public method .new() {
    arg [args];
    
    .perms(caller(), $event_handler);
    
    // we still don't know what we want
    //  if (type(location) != 'objnum)
    //    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 method .dispatch() {
    arg dict;
    
    (dict['where]).event((<this(), dict>));
};

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

public method .type() {
    arg frob;
    
    return type;
};

public method .process() {
    arg [args];
    
};


new object $movement_event: $event_frob;

var $root inited = 1;
var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'variables, 'core];
var $event_frob type = 'movement;
var $root manager = $movement_event;
var $root managed = [$movement_event];
var $root owners = [$movement_event];
var $root owned = [$movement_event];

public method .exit() {
    arg event;
    
    return event['exit];
};

public method .new() {
    arg actor, source, dest;
    
    //  (> .perms(caller(), $exit) <);
    return (<this(), #[['exit, sender()], ['actor, actor], ['source, source], ['dest, dest]]>);
};

public method .dest() {
    arg frob;
    
    return frob['dest];
};

public method .source() {
    arg args;
    
    return (args[3]).source();
};

public method .dispatch() {
    arg dict;
    
    (dict['source]).announce_event((<this(), dict>));
    (dict['dest]).announce_event((<this(), dict>));
};

public method .process() {
    arg dict;
    
};

public method .actor() {
    arg frob;
    
    return frob['actor];
};


new object $logic_frob: $frob;

var $root child_index = 6;
var $root trusted = [];
var $root inited = 1;
var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'variables, 'core];
var $root manager = $logic_frob;
var $root managed = [$logic_frob];
var $root owners = [$logic_frob];
var $root owned = [$logic_frob];

public method .test() {
    arg [args];
    
    return 0;
};


new object $xor: $logic_frob;

var $root trusted = [];
var $root inited = 1;
var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'variables, 'core];
var $root manager = $xor;
var $root managed = [$xor];
var $root owners = [$xor];
var $root owned = [$xor];

public method .unparse() {
    arg xorlist;
    var str, x;
    
    str = "";
    for x in (xorlist) {
        catch any
            str = (str + (x.unparse())) + " ^^ ";
        with
            str = (str + tostr(x)) + " ^^ ";
    }
    return ("(" + (str && (str.subrange(1, (str.length()) - 4)))) + ")";
};

public method .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
            val = val ? !x : x;
    }
    return val;
};


new object $and: $logic_frob;

var $root trusted = [];
var $root inited = 1;
var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'variables, 'core];
var $root manager = $and;
var $root managed = [$and];
var $root owners = [$and];
var $root owned = [$and];

public method .unparse() {
    arg andlist;
    var str, x;
    
    str = "";
    for x in (andlist) {
        catch any
            str = (str + (x.unparse())) + " && ";
        with
            str = (str + tostr(x)) + " && ";
    }
    return ("(" + (str && (str.subrange(1, (str.length()) - 4)))) + ")";
};

public method .test() {
    arg andlist, [args];
    var val, x;
    
    val = 0;
    for x in (andlist) {
        catch ~type, ~methodnf
            val = x.test(@args);
        with
            val = x;
        if (!val)
            break;
    }
    return val;
};


new object $lock_frob: $logic_frob;

var $root child_index = 2;
var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'variables, 'core];
var $lock_frob names = 0;
var $root manager = $lock_frob;
var $root managed = [$lock_frob];
var $root owners = [$lock_frob];
var $root owned = [$lock_frob];

public method .lock_name() {
    arg value, [type];
    
    type = [@type, 'default][1];
    if (type == 'literal)
        type = 'default;
    return (| names[type] |) || (names['default]);
};

public method .set_lock_name() {
    arg name;
    
    (> .perms(sender(), 'manager) <);
    lock_name = name;
};

public method .add_name() {
    arg type, name;
    
    (> .perms(sender(), 'manager) <);
    if (!names)
        names = #[];
    names = names.add(type, name);
};


new object $or_lock_frob: $lock_frob;

var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'variables, 'core];
var $root manager = $or_lock_frob;
var $root managed = [$or_lock_frob];
var $root owners = [$or_lock_frob];
var $root owned = [$or_lock_frob];

public method .new() {
    arg lhs, rhs;
    
    if ((type(lhs) != 'frob) || (type(rhs) != 'frob))
        throw(~perm, "Arguments are not both frobs.");
    return (<this(), [lhs, rhs]>);
};

public method .try() {
    arg lock, obj;
    
    return ((lock[1]).try(obj)) || ((lock[2]).try(obj));
};

public method .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));
    }
};


new object $true_lock_frob: $lock_frob;

var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'variables, 'core];
var $lock_frob names = #[['default, "true"], ['exit, "anybody"], ['str, "any"]];
var $root manager = $true_lock_frob;
var $root managed = [$true_lock_frob];
var $root owners = [$true_lock_frob];
var $root owned = [$true_lock_frob];

public method .new() {
    return (<this(), []>);
};

public method .try() {
    arg lock, obj;
    
    return 1;
};


new object $false_lock_frob: $lock_frob;

var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'variables, 'core];
var $lock_frob names = #[['str, "none"], ['exit, "nobody"], ['default, "false"]];
var $root manager = $false_lock_frob;
var $root managed = [$false_lock_frob];
var $root owners = [$false_lock_frob];
var $root owned = [$false_lock_frob];

public method .new() {
    return (<this(), []>);
};

public method .try() {
    arg lock, obj;
    
    return 0;
};


new object $not_lock_frob: $lock_frob;

var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'variables, 'core];
var $root manager = $not_lock_frob;
var $root managed = [$not_lock_frob];
var $root owners = [$not_lock_frob];
var $root owned = [$not_lock_frob];

public method .new() {
    arg lock;
    
    if (type(lock) != 'frob)
        throw(~perm, "Argument is not a lock.");
    return (<this(), [lock]>);
};

public method .try() {
    arg lock, obj;
    
    return !((lock[1]).try(obj));
};

public method .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));
    }
};


new object $object_lock_frob: $lock_frob;

var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'variables, 'core];
var $root manager = $object_lock_frob;
var $root managed = [$object_lock_frob];
var $root owners = [$object_lock_frob];
var $root owned = [$object_lock_frob];

public method .new() {
    arg obj;
    
    if (type(obj) != 'objnum)
        throw(~perm, "Argument is not a dbref.");
    return (<this(), [obj]>);
};

public method .try() {
    arg lock, obj;
    
    return ((lock[1]) == obj) || ((obj == sender()) || ($sys.is_system(obj)));
};

public method .test() {
    arg testlist, testee, [args];
    var x;
    
    for x in (testlist) {
        if (testee == x)
            return 1;
    }
    return 0;
};

public method .lock_name() {
    arg value, [type];
    
    return (value[1]).name();
};


new object $and_lock_frob: $lock_frob;

var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'variables, 'core];
var $root manager = $and_lock_frob;
var $root managed = [$and_lock_frob];
var $root owners = [$and_lock_frob];
var $root owned = [$and_lock_frob];

public method .new() {
    arg lhs, rhs;
    
    if ((type(lhs) != 'frob) || (type(rhs) != 'frob))
        throw(~perm, "Arguments are not both frobs.");
    return (<this(), [lhs, rhs]>);
};

public method .try() {
    arg lock, obj;
    
    return ((lock[1]).try(obj)) && ((lock[2]).try(obj));
};

public method .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));
    }
};


new object $inside_lock_frob: $lock_frob;

var $root trusted = [];
var $root inited = 1;
var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'variables, 'core];
var $root manager = $inside_lock_frob;
var $root managed = [$inside_lock_frob];
var $root owners = [$inside_lock_frob];
var $root owned = [$inside_lock_frob];

public method .try() {
    arg objs, obj;
    var o;
    
    for o in (objs) {
        if (o.contains(obj))
            return 1;
    }
    return 0;
};


new object $not: $logic_frob;

var $root trusted = [];
var $root inited = 1;
var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'variables, 'core];
var $root manager = $not;
var $root managed = [$not];
var $root owners = [$not];
var $root owned = [$not];

public method .unparse() {
    arg notlist;
    
    catch any {
        catch ~type, ~methodnf
            return "!" + ((notlist[1]).unparse());
        with
            return "!" + tostr(notlist[1]);
    } with {
        return "!()";
    }
};

public method .test() {
    arg notlist, [args];
    var val;
    
    catch ~range {
        catch ~type, ~methodnf
            return !((notlist[1]).test(@args));
        with
            return !(notlist[1]);
    } with {
        return 1;
    }
};


new object $or: $logic_frob;

var $root trusted = [];
var $root inited = 1;
var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'variables, 'core];
var $root manager = $or;
var $root managed = [$or];
var $root owners = [$or];
var $root owned = [$or];

public method .unparse() {
    arg orlist;
    var str, x;
    
    str = "";
    for x in (orlist) {
        catch any
            str = (str + (x.unparse())) + " || ";
        with
            str = (str + tostr(x)) + " || ";
    }
    return ("(" + (str && (str.subrange(1, (str.length()) - 4)))) + ")";
};

public method .test() {
    arg orlist, [args];
    var val, x;
    
    val = 0;
    for x in (orlist) {
        catch ~type, ~methodnf
            val = x.test(@args);
        with
            val = x;
        if (val)
            break;
    }
    return val;
};


new object $true: $logic_frob;

var $root trusted = [];
var $root inited = 1;
var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'variables, 'core];
var $root manager = $true;
var $root managed = [$true];
var $root owners = [$true];
var $root owned = [$true];

public method .unparse() {
    arg dummy;
    
    return "1";
};

public method .test() {
    arg [args];
    
    return 1;
};


new object $false: $logic_frob;

var $root trusted = [];
var $root inited = 1;
var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'variables, 'core];
var $root manager = $false;
var $root managed = [$false];
var $root owners = [$false];
var $root owned = [$false];

public method .unparse() {
    arg dummy;
    
    return "0";
};

public method .test() {
    arg [args];
    
    return 0;
};


new object $realms_frob: $frob, $named, $world;

var $root inited = 1;
var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'variables, 'core];
var $named name = ['uniq, "realms_frob", "the realms_frob"];
var $named name_aliases = [];
var $realms_frob local = #[['weather, 0], ['weather_text, <$j_ctext_frob, [[], #[['this, #4]]]>], ['map_position, [0, 0, "00"]], ['map_object, $generic_map], ['climate, $climate], ['location, 'interior]];
var $realms_frob types = #[['weather, 'integer], ['weather_text, 'ctext], ['map_position, 'map_position], ['map_object, 'object], ['climate, 'object], ['location, 'intext]];
var $root manager = $realms_frob;
var $root managed = [$realms_frob];
var $root owners = [$realms_frob];
var $root owned = [$realms_frob];

public method .new() {
    arg [args];
    
    args = [@args, ['interior]][1];
    (> $places.is_place(sender()) <);
    local = local.add(sender(), args);
    return (<this(), args>);
    
    // $#Edited: 12 Aug 96 17:54 $jenner
};

public method .destroyed() {
    arg frob;
    
    // $#Edited: 12 Aug 96 17:54 $jenner
};

public method .init_realms_frob() {
    local = #[];
    
    // $#Edited: 12 Aug 96 17:54 $jenner
};

root method .uninit_realms_frob() {
    var x;
    
    for x in (local)
        (x[1]).set_realm($realm_of_creation, 'interior);
    
    // $#Edited: 12 Aug 96 17:54 $jenner
};

public method .place_destroyed() {
    arg place;
    var x;
    
    (sender() == (.manager())) || (> .perms(caller(), $place) <);
    (| (local = local.del(place)) |);
    for x in (local.keys())
        (| x.place_destroyed(place) |);
    (| pass(place) |);
    
    // $#Edited: 12 Aug 96 17:54 $jenner
};

public method .local() {
    return local;
    
    // $#Edited: 12 Aug 96 17:54 $jenner
};

public method .name() {
    arg [article];
    
    if (article && (type(article[1]) == 'list))
        article = article.delete(1);
    return (> pass(@article) <);
    
    // $#Edited: 12 Aug 96 17:54 $jenner
};

public method .realm_name() {
    arg [args];
    var realms;
    
    realms = .realms();
    while ((realms.length()) > 2)
        realms = realms.delete(2);
    return realms.join(", ");
    
    // $#Edited: 12 Aug 96 17:54 $jenner
};

public method .realms() {
    arg [args];
    var r;
    
    if (definer() == this())
        return [];
    return [.name()] + (((.parents())[1]).realms());
    
    // $#Edited: 12 Aug 96 17:54 $jenner
};

public method .get_realm_var() {
    arg data, v, [def];
    var realms;
    
    if (type((| data[v] |)) != 'error)
        return data[v];
    if (type((| local[v] |)) != 'error)
        return local[v];
    if (definer() != this())
        return (> ((.parents())[1]).get_realm_var(data, v, @def) <);
    if (def)
        return def[1];
    throw(~keynf, "Realm variable not found: " + v);
    
    // $#Edited: 22 Aug 96 20:40 $jenner
};

public method .all_realm_vars() {
    arg [data];
    var k;
    
    return hash k in ((.all_realm_types()).keys()) to ([k, .get_realm_var((| data[1] |), k, ~undefined)]);
    
    // $#Edited: 22 Aug 96 21:01 $jenner
};

public method .unparse_variable() {
    arg data, v, [def];
    var value, t;
    
    value = (> .get_realm_var(data, v, @def) <);
    t = (> .type_of(v) <);
    switch (t) {
        case 'ctext:
            return value.uncompile();
        case 'boolean, 'integer:
            return tostr(value);
        case 'string, 'text:
            return value;
        default:
            return (> .(tosym("unparse_" + t))(value) <);
    }
    
    // $#Edited: 22 Aug 96 20:53 $jenner
};

public method .set_realm_var() {
    arg [args];
    
    // GRR, hack; grammar for frobs doesnt like complex expressions as
    // the value (-Brandon)
    if ((args.length()) == 3) {
        if ((args[3]) == 'unset)
            return (<this(), dict_del(@delete(args, 3))>);
        else
            return (<this(), dict_add(@args)>);
    }
    (> .perms(sender()) <);
    if ((args[2]) == 'unset)
        local = dict_del(local, args[1]);
    else
        local = dict_add(local, @args);
    
    // $#Edited: 13 Aug 96 17:48 $brandon
};

public method .type_of() {
    arg v;
    var t;
    
    if (!(| (t = types[v]) |))
        return (definer() == this()) ? 0 : (((.parents())[1]).type_of(v));
    return t;
    
    // $#Edited: 12 Aug 96 17:54 $jenner
};

public method .parse_variable() {
    arg v, str;
    var value, t;
    
    t = (> .type_of(v) <);
    switch (t) {
        case 'ctext:
            return $j_compiler.compile_cml(str);
        case 'boolean:
            if (type(str) == 'list)
                str = str.join();
            if (str in ["1", "t", "true", "y", "yes"])
                return "yes";
            if (str in ["0", "f", "false", "n", "no"])
                return "no";
            throw(~parse, "Illegal boolean value");
        case 'string:
            if (type(str) == 'list)
                str = str.join();
            return str;
        case 'text:
            if (type(str) == 'string)
                str = [str];
            return str;
        default:
            return (> .(tosym("parse_" + t))(str) <);
    }
    
    // $#Edited: 12 Aug 96 17:54 $jenner
};

public method .unparse_map_position() {
    arg value;
    
    return "%l,%l,%l".format(@value);
    
    // $#Edited: 12 Aug 96 17:54 $jenner
};

public method .parse_map_position() {
    arg str;
    
    // The format is x,y,zz (coordinates of the window into the map,
    // tag to be printed)
    if (type(str) == 'list)
        str = str.join();
    str = str.explode(",");
    return (> [toint(str[1]), toint(str[2]), str[3]] <);
    
    // $#Edited: 12 Aug 96 17:54 $jenner
};

public method .unparse_object() {
    arg value;
    
    return tostr(value);
    
    // $#Edited: 12 Aug 96 17:54 $jenner
};

public method .parse_object() {
    arg value;
    
    return (| lookup(tosym(value.subrange(2))) |) || (> sender().match_environment(value) <);
    
    // $#Edited: 12 Aug 96 17:54 $jenner
};

public method .add_realm_variable() {
    arg v, type;
    
    (> .perms(sender(), 'manager) <);
    types = types.add(v, type);
    
    // $#Edited: 12 Aug 96 17:54 $jenner
};

public method .del_realm_variable() {
    arg v, type;
    
    (> .perms(sender(), 'manager) <);
    types = types.del(v);
    
    // $#Edited: 12 Aug 96 17:54 $jenner
};

public method .unparse_intext() {
    arg value;
    
    return #[['interior, "interior"], ['exterior, "exterior"]][value];
    
    // $#Edited: 12 Aug 96 18:09 $jenner
};

public method .parse_intext() {
    arg str;
    
    // either interior or exterior
    if (str.match_template("i?nterior"))
        return 'interior;
    else if (str.match_template("e?xterior"))
        return 'exterior;
    else
        throw(~parse, "This variable should be either \"i?nterior\" or \"e?xterior\"");
    
    // $#Edited: 12 Aug 96 18:09 $jenner
};

public method .all_realm_types() {
    arg [parent_types];
    var tmp;
    
    parent_types = parent_types ? parent_types[1] : #[];
    if (!types)
        types = #[];
    tmp = types.ordered_union(parent_types);
    return (definer() == this()) ? tmp : (((.parents())[1]).all_realm_types(tmp));
    
    // $#Edited: 22 Aug 96 21:01 $jenner
};

public method .all_realm_vars_text() {
    arg [data];
    var k;
    
    return map k in ((.all_realm_types()).keys()) to ([("var " + tostr(k)) + " = "].affix(.unparse_variable((| data[1] |), k, ~undefined)));
    
    // $#Edited: 22 Aug 96 21:01 $jenner
};


new object $realm_of_creation: $realms_frob;

var $root inited = 1;
var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'variables, 'core];
var $named name = ['prop, "<Creation>", "<Creation>"];
var $realms_frob local = #[[#362, ['interior]], [#305, ['interior]], [#306, ['interior]], [#310, ['interior]], [$frob, ['interior]], [$network, ['interior]], [#461, ['interior]], [#311, ['interior]], [#460, ['interior]], [#462, ['interior]], [#657, ['interior]], [#315, ['interior]], [#1800, ['interior]], [#464, ['interior]], [#465, ['interior]], [#466, ['interior]]];
var $root manager = $realm_of_creation;
var $realms_frob types = #[];
var $root managed = [$realm_of_creation];
var $root owners = [$realm_of_creation];
var $root owned = [$realm_of_creation];


new object $ctext_frob: $frob;

var $root manager = $ctext_frob;
var $root inited = 1;
var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'variables, 'core];
var $root managed = [$ctext_frob];
var $root owners = [$ctext_frob];
var $root owned = [$ctext_frob];

public method ._ctext() {
    arg this;
    
    return this;
};

public method .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 method .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 method .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) == 'objnum) {
            line = line + term;
        } else if (type(term) == 'list) {
            if ((term[1]) == 'b_stmt) {
                output = [@output, line];
                line = "";
            } else {
                output = [@output, line, term.join()];
                line = "";
            }
        } else {
            line = line + toliteral(term);
        }
    }
    output = [@output, line];
    return output;
    
    // $#Edited: 12 Jun 96 20:02 $levi
    // $#Edited: 18 Jul 96 14:56 $levi
};

public method .uncompile() {
    arg this;
    
    return $uncompile_evaluator.eval_ctext(this);
};

public method .ctext() {
    arg this;
    
    return this;
};

public method .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>);
};


new object $ctext_tag: $frob;

var $root child_index = 2;
var $root manager = $ctext_tag;
var $root owners = [$ctext_tag];
var $root created_on = 806278619;
var $root inited = 1;
var $root flags = ['methods, 'code, 'variables, 'core];
var $root managed = [$ctext_tag];
var $root owned = [$ctext_tag];

public method .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 method .name() {
    arg self;
    
    // if (type(self)=='dictionary)
    // return self['name];
    //else
    return self[1];
};

public method .uflags() {
    arg self;
    
    //if (type(self)=='dictionary)
    // return (| self['uflags]|) || #[];
    //else
    return self[3];
};

public method .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 method .args() {
    arg self;
    
    //if (type(self)=='dictionary)
    // return (| self['args]|) || [];
    //else
    return self[4];
};

public method .set_args() {
    arg self, args;
    
    //if (type(self) == 'dictionary)
    // return <this(), self.add('args,args)>;
    //else
    return (<this(), self.replace(4, args)>);
};

public method .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 .method() {
    arg self;
    
    return self[5];
};

public method .ctext_flags() {
    arg self;
    
    //if (type(self)=='dictionary)
    // return ((| self['flags]|) || #[]).union((|self['uflags]|) || #[]);
    //else
    return (self[2]).union(self[3]);
};

public method .ctext_uflags() {
    arg self;
    
    //if (type(self)=='dictionary)
    // return (| self['uflags]|) || #[];
    //else
    return self[3];
};

public method .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>);
};

public method .tag_flags() {
    arg self;
    
    //if (type(self)=='dictionary)
    // return ((| self['flags]|) || #[]).union((|self['uflags]|) || #[]);
    //else
    return (self[2]).union(self[3]);
    
    // $#Edited: 15 Oct 95 22:22 Jeff ($jeff)
};


new object $ctext_generator: $ctext_tag;

var $root manager = $ctext_generator;
var $root owners = [$ctext_generator];
var $root created_on = 806278855;
var $root inited = 1;
var $root flags = ['methods, 'code, 'variables, 'core];
var $root managed = [$ctext_generator];
var $root owned = [$ctext_generator];

public method .new() {
    arg name, flags, args;
    
    return pass(name, flags, args, tosym("gen_" + name));
};

public method .method() {
    arg self;
    
    if (type(self) == 'dictionary)
        return tosym("gen_" + (self['name]));
    else
        return pass(self);
};


new object $ctext_format: $ctext_tag;

var $root manager = $ctext_format;
var $root owners = [$ctext_format];
var $root created_on = 806278844;
var $root inited = 1;
var $root flags = ['methods, 'code, 'variables, 'core];
var $root managed = [$ctext_format];
var $root owned = [$ctext_format];

public method .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 .method() {
    arg self;
    
    if (type(self) == 'dictionary)
        return tosym("do_" + (self['name]));
    else
        return pass(self);
};

public method .new() {
    arg name, flags, args;
    
    return pass(name, flags, args, tosym("do_" + name));
};


new object $thing_frob: $frob, $thing;

var $root child_index = 2;
var $root fertile = 1;
var $root inited = 1;
var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'fertile, 'variables, 'core];
var $located location = $void;
var $located obvious = 1;
var $described prose = [];
var $gendered gender = $gender_neuter;
var $named name = ['uniq, "thing_class", "the thing_class"];
var $named name_aliases = [];
var $root manager = $thing_frob;
var $root managed = [$thing_frob];
var $root owners = [$thing_frob];
var $root owned = [$thing_frob];

public method ._match_name() {
    arg data, str;
    
    return match_begin((data['name])[2], str);
};

public method .match_name() {
    arg [args];
    
    if (args && (type(args[1]) == 'dictionary))
        return (> ._match_name(@args) <);
    return (> pass(@args) <);
};

public method .location() {
    arg [args];
    
    if (args)
        return (| (args[1])['location] |) || $nowhere;
    else
        return pass(@args);
};

public method .did_move() {
    arg [args];
    
    if (type(args[1]) == 'dictionary)
        return (<this(), (args[1])[1]>);
    pass(@args);
};

public method ._will_move() {
    arg dict, mover, place;
    
    if ((| dict['lock] |) && (!((dict['lock]).try(mover))))
        throw(~locked, ((((.name()).capitalize()) + " is locked to ") + ((dict['lock]).lock_name('thing))) + ".");
    return 1;
};

public method .match_name_exact() {
    arg [args];
    
    if (args && (type(args[1]) == 'dictionary))
        return (> ._match_name(@args) <);
    return (> pass(@args) <);
};

public method .is_obvious_to() {
    arg [args];
    
    return 1;
};

public method .match_name_aliases() {
    arg [args];
    
    if (args && (type(args[1]) == 'dictionary))
        return (> ._match_name(@args) <);
    return (> pass(@args) <);
};

public method .will_move() {
    arg [args];
    var lock;
    
    if (type(args[1]) == 'dictionary)
        return (> ._will_move(@args) <);
    return (> pass(@args) <);
};

public method .prose() {
    arg [args];
    var dict;
    
    if (args && (type(args[1]) == 'dictionary))
        return (| dict['prose] |) || "You see nothing special.";
    return (> pass(@args) <);
};

public method .namef() {
    arg [args];
    
    if (args && (type(args[1]) == 'dictionary))
        return .name(@args);
    return (> pass(@args) <);
};

public method ._name() {
    arg data, [args];
    var name;
    
    name = (| data['name] |) || (.name('literal));
    if (!name)
        return tostr(this());
    if (!args)
        return name[3];
    switch (args[1]) {
        case 'type:
            return name[1];
        case 'noarticle:
            return name[2];
        default:
            return name;
    }
};

public method ._change_data() {
    arg old_data, new_data;
    
    if (valid(old_data['location])) {
        (old_data['location]).del_frob_from_contents((<this(), old_data>));
        if (valid(new_data['location]))
            (new_data['location]).add_frob_to_contents((<this(), new_data>));
    }
    return (<this(), new_data>);
};

public method .has_ancestor() {
    arg arg1, [arg2];
    
    if (arg2)
        arg1 = arg2[1];
    return (> pass(arg1) <);
    
    // $# Edited 19 Oct 1995 13:12 Lynx ($lynx)
};

protected method .description() {
    arg [args];
    
    if (listlen(args) == 1)
        return ._description(@args);
    return (> pass(@args) <);
};

public method ._description() {
    arg data, flags;
    var out, flags, name, data;
    
    out = (<$j_ctext_frob, [[(<$j_format, ["subj", [], [.name()], 'do_subj]>)], #[]]>);
    if (flags['prose])
        return [out, ._prose()];
    return [out];
    
    // $#Edited: 18 Jul 96 20:15 $jenner
};

public method .get_description() {
    arg [args];
    var sender, def_flags, flags;
    
    // if !2 args assume it isn't a frob..
    if (listlen(args) != 2)
        return (> pass(@args) <);
    return ._get_description(@args);
};

public method .look_at_cmd() {
    arg [args];
    
    if (type(args[1]) == 'dictionary)
        return ._get_description(args[1], #[['actor, sender()], ['exclude, this()]]);
    return (> pass(@args) <);
};

public method .all_remote_commands() {
    arg [args];
    
    return pass();
};

public method .remote_commands() {
    arg [args];
    
    return pass();
};

public method .set_prose() {
    arg [args];
    var data, prose, location;
    
    if (type(args[1]) == 'dictionary) {
        if (((args[1])['manager]) != sender())
            throw(~perm, "You are not the manager of " + (._name(args[1])));
        return ._change_data(args[1], (args[1]).add('prose, args[2]));
    } else {
        return (> pass(@args) <);
    }
};

public method .set_frob_manager() {
    arg data, new;
    
    if (sender() != (data['manager]))
        throw(~perm, ((sender().name()) + " is not the manager of ") + (._name(data)));
    return ._change_data(data, data.add('manager, new));
};

public method .set_name() {
    arg [args];
    var name, new, data, type, loc;
    
    if (type(args[1]) != 'dictionary)
        return (> pass(@args) <);
    data = args[1];
    new = args[2];
    if (!new)
        return;
    if (new && ((new[1]) in ["$", "#"]))
        throw(~invname, "Names cannot begin with \"$\" or \"#\".");
    if (match_regexp(new, "^(a|an|the) +"))
        throw(~bad_name, "Do not include articles in name, use +u +n or +p instead.");
    type = (| args[3] |) || 'normal;
    switch (type) {
        case 'prop:
            new = [type, new, new];
        case 'uniq:
            new = [type, new, "the " + new];
        case 'normal:
            new = [type, new, ((new.a_or_an()) + " ") + new];
        default:
            throw(~invarg, "Type must be one of: 'prop, 'normal or 'uniq.");
    }
    return ._change_data(data, data.add('name, new));
};

public method .new() {
    arg [args];
    var name, prose, data, n;
    
    name = (listlen(args) > 0) ? args[1] : (.name('literal));
    prose = (listlen(args) > 1) ? args[2] : ['prose, []];
    if (type(name) == 'string) {
        name = (> $code_lib.parse_name(name) <);
        n = (name[1])[1];
        switch ((name[1])[2]) {
            case 'normal:
                name = ['normal, n, n];
            case 'uniq:
                name = ['normal, n, "the " + n];
            case 'prop:
                name = ['normal, n, ((n.a_or_an()) + " ") + n];
        }
    }
    data = #[['name, name], ['prose, prose], ['location, sender()], ['manager, sender()]];
    return ._change_data(data, data);
};

public method ._get_description() {
    arg data, flags;
    
    flags = flags.union($code_lib.default_description_flags());
    return ._description(data, flags);
};

public method .name() {
    arg [args];
    
    if (args && (type(args[1]) == 'dictionary))
        return ._name(@args);
    return (> pass(@args) <);
};

public method .name_aliases() {
    arg [args];
    
    if (args)
        return [];
    return pass();
};

public method .get_command_info() {
    arg [args];
    
    if (type(args[1]) == 'dictionary)
        return (> pass(@sublist(args, 2)) <);
    return pass(@args);
};

public method .discard() {
    arg data;
    
    if (sender() != (data['manager]))
        throw(~perm, (sender().name()) + " is not the manager.");
    (data['location]).del_frob_from_contents((<this(), data>));
    
    // $#Edited: 24 Apr 96 22:35 $lynx
};

public method ._move_to() {
    arg data, place;
    var old, dict, location;
    
    if (!(place.has_ancestor($location)))
        throw(~type, "Argument is not a location.");
    location = data['location];
    if (!valid(location))
        location = $nowhere;
    (> ._will_move(data, sender(), place) <);
    return ._change_data(data, data.add('location, place));
    
    // $#Edited: 25 Apr 96 17:53 $lynx
};

public method .move_to() {
    arg [args];
    
    if (type(args[1]) == 'dictionary)
        return (> ._move_to(@args) <);
    return (> pass(@args) <);
    
    // $#Edited: 25 Apr 96 17:53 $lynx
};


new object $wearable_frob: $thing_frob;

var $root child_index = 2;
var $root fertile = 1;
var $root inited = 1;
var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'fertile, 'variables, 'core];
var $described prose = [];
var $named name = ['normal, "Generic Wearable Frob", "a Generic Wearable Frob"];
var $has_commands remote = #[["wear", [["wear", "*", "wear <this>", 'wear_cmd, #[[1, ['this, []]]]]]], ["remove", [["remove", "*", "remove <this>", 'remove_cmd, #[[1, ['this, []]]]]]]];
var $root manager = $wearable_frob;
var $located location = $lost_and_found;
var $root managed = [$wearable_frob];
var $root owners = [$wearable_frob];
var $root owned = [$wearable_frob];

public method .wear() {
    arg dict;
    
    sender().wear(.to_frob(dict));
};

public method .wear_cmd() {
    arg [args];
    
};

public method .remove_cmd() {
    arg [args];
    
};


new object $read_parser: $frob;

var $root inited = 1;
var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'variables, 'core];
var $root manager = $read_parser;
var $root managed = [$read_parser];
var $root owners = [$read_parser];
var $root owned = [$read_parser];

public method .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);
    
        //// No, bad Brandon, Bad, no scooby snack
        //// Decomment this if you want '>' to escape commands when reading
        //   } 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 method .new() {
    arg task_id, count;
    
    return (<this(), #[['lines, []], ['status, 'not_done], ['count, count], ['task_id, task_id]]>);
};

public method .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)>);
    }
    
    // $#Edited: 08 Jan 96 18:31 Lynx ($lynx)
};

public method .status() {
    arg dict;
    
    return dict['status];
};

public method .task_id() {
    arg dict;
    
    return dict['task_id];
};

public method .lines() {
    arg dict;
    
    return dict['lines];
};

public method .command() {
    arg dict;
    
    return (| dict['command] |) || "";
};

public method .add() {
    arg dict, [args];
    
    return (<this(), (> dict.add(@args) <)>);
};


new object $callback: $frob;

var $root manager = $callback;
var $root owners = [$callback];
var $root created_on = 799275808;
var $root inited = 1;
var $root flags = ['methods, 'code, 'variables, 'core];
var $root managed = [$callback];
var $root owned = [$callback];

public method .new() {
    arg method, static_args;
    
    return (<this(), [sender(), method, static_args]>);
};

public method .exec() {
    arg self, [args];
    
    return (self[1]).(self[2])(self[3], args);
};


new object $trie: $frob;

var $root manager = $trie;
var $root help_node = #665;
var $root created_on = 800074237;
var $root inited = 1;
var $root flags = ['methods, 'code, 'variables, 'core];
var $root managed = [$trie];
var $root owners = [$trie];
var $root owned = [$trie];

public method .match_begin() {
    arg trie, key;
    var n, t;
    
    if ((trie[1]) && ((key == ((trie[1])[1])) || (((trie.length()) == 2) && match_begin((trie[1])[1], key))))
        return trie[1];
    if (!key)
        throw(~ambig, "Trie: ambiguous match.");
    if (!(n = (key[1]) in (trie[2])))
        throw(~keynf, "Trie: key not found.");
    (> (t = .match_begin(trie[n + 2], key.subrange(2))) <);
    t = t.replace(1, (key[1]) + (t[1]));
    return t;
    
    // $#Edited: 21 Apr 96 14:20 Jenner ($jenner)
};

public method .add() {
    arg [args];
    
    return (<$trie, ._add(@args)>);
    
    // $#Edited: 18 Apr 96 23:59 Jenner ($jenner)
};

public method .del() {
    arg [args];
    
    return (<$trie, ._del(@args)>);
    
    // $#Edited: 21 Apr 96 13:47 Jenner ($jenner)
};

public method ._add() {
    arg trie, key, [values];
    var n, word;
    
    // This still ain't working. Current problem: values get mingled
    if (trie[1]) {
        if (key == ((trie[1])[1]))
            return trie.replace(1, [key, @values]);
        word = (trie[1])[1];
        if (word) {
            if (!(n = (word[1]) in (trie[2])))
                trie = [@trie.replace(2, (trie[2]) + (word[1])), ._add([0, ""], word.subrange(2), @(trie[1]).subrange(2))];
            else
                trie = trie.replace(n + 2, ._add(trie[n + 2], word.subrange(2), @(trie[1]).subrange(2), @values));
            trie = trie.replace(1, 0);
        }
    }
    if (((!(trie[1])) && ((trie.length()) == 2)) || (!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));
    
    // $#Edited: 20 Apr 96 21:55 Jenner ($jenner)
};

public method ._del() {
    arg trie, key;
    var n, t1;
    
    if ((trie[1]) && (key == ((trie[1])[1]))) {
        trie = trie.replace(1, 0);
        if (((trie.length()) == 3) && (!((trie[3])[2])))
            trie = [((trie[3])[1]).replace(1, (trie[2]) + (((trie[3])[1])[1])), ""];
        return trie;
    }
    if (!key)
        throw(~ambig, "Trie: Can't delete more than one key.");
    if (!(n = (key[1]) in (trie[2])))
        throw(~keynf, "Trie: No such key.");
    t1 = (> ._del(trie[n + 2], key.subrange(2)) <);
    if (t1 == [0, ""])
        trie = (trie.delete(n + 2)).replace(2, ((trie[2]).subrange(1, n - 1)) + ((trie[2]).subrange(n + 1)));
    else
        trie = trie.replace(n + 2, t1);
    if (((trie.length()) == 3) && ((!(trie[1])) && (!((trie[3])[2]))))
        trie = [((trie[3])[1]).replace(1, (trie[2]) + (((trie[3])[1])[1])), ""];
    return trie;
    
    // $#Edited: 21 Apr 96 14:07 Jenner ($jenner)
};

public method .match_exact() {
    arg trie, key;
    var n, t;
    
    if ((trie[1]) && (key == ((trie[1])[1])))
        return trie[1];
    if ((!key) || (!(n = (key[1]) in (trie[2]))))
        throw(~keynf, "Trie: key not found.");
    (> (t = .match_exact(trie[n + 2], key.subrange(2))) <);
    t = t.replace(1, (key[1]) + (t[1]));
    return t;
    
    // $#Edited: 21 Apr 96 14:29 Jenner ($jenner)
};

public method .keys() {
    arg trie, [prefix];
    var n, i, l;
    
    prefix = [@prefix, ""][1];
    l = (trie[1]) ? [prefix + ((trie[1])[1])] : [];
    if (trie[2]) {
        for i in [1 .. (trie[2]).length()]
            l = [@l, @.keys(trie[2 + i], prefix + ((trie[2])[i]))];
        refresh();
    }
    return l;
    
    // $#Edited: 21 Apr 96 14:37 Jenner ($jenner)
};

public method .to_dict() {
    arg trie, [prefix];
    var n, i, dict;
    
    // This function will only convert single-valued tries (such as were probably obtained from dictionaries
    prefix = [@prefix, ""][1];
    dict = (trie[1]) ? #[[prefix + ((trie[1])[1]), (trie[1])[2]]] : #[];
    if (trie[2]) {
        for i in [1 .. (trie[2]).length()]
            dict = dict.union(.to_dict(trie[2 + i], prefix + ((trie[2])[i])));
        refresh();
    }
    return dict;
};


new object $j_ctext_frob: $frob;

var $root manager = $j_ctext_frob;
var $root flags = ['variables, 'methods, 'code, 'core];
var $root created_on = 837584074;
var $root owners = [$j_ctext_frob];
var $root inited = 1;
var $root managed = [$j_ctext_frob];
var $root owned = [$j_ctext_frob];

public method .new() {
    arg data, [vars];
    
    vars = [@vars, #[]][1];
    return (<this(), [data, vars]>);
    
    // $#Edited: 16 Jul 96 08:12 $kahuna
    // $#Edited: 17 Jul 96 00:05 $kahuna
    // $#Edited: 17 Jul 96 00:14 $jenner
};

public method .eval_ctext() {
    arg this;
    var data, vars;
    
    vars = this[2];
    vars = vars.add('time, 'pre);
    if (!(| vars['sender] |))
        vars = vars.add('sender, sender());
    if (!(| vars['evaluator] |))
        vars = vars.add('evaluator, $j_bs_eval);
    vars = vars.union((vars['evaluator]).init());
    return .new(@(vars['evaluator])._eval_ctext(vars, this[1]));
    
    // $#Edited: 16 Jul 96 08:12 $kahuna
    // $#Edited: 16 Jul 96 10:37 $kahuna
    // $#Edited: 17 Jul 96 00:05 $kahuna
    // $#Edited: 17 Jul 96 00:14 $jenner
};

public method .format() {
    arg this;
    var vars, str;
    
    vars = this[2];
    vars = vars.add('time, 'post);
    if (!(| vars['receiver] |))
        vars = vars.add('receiver, sender());
    if (!(| vars['formatter] |))
        vars = vars.add('formatter, $j_telnet_format);
    vars = vars.union((vars['formatter]).init());
    str = ((vars['formatter])._eval_ctext(vars, this[1]))[1];
    if (((str.length()) < 2) || ((str.subrange((str.length()) - 1)) != "\n"))
        str += "\n";
    return $buffer.from_string(str);
    
    // $#Edited: 01 Aug 96 21:49 $jenner
};

public method .set_var() {
    arg this, name, value;
    var vars;
    
    vars = this[2];
    vars = vars.add(name, value);
    return (<this(), [this[1], vars]>);
    
    // $#Edited: 16 Jul 96 08:12 $kahuna
    // $#Edited: 17 Jul 96 00:05 $kahuna
    // $#Edited: 17 Jul 96 00:14 $jenner
};

public method .get_var() {
    arg this, name;
    
    return (this[2])[name];
    
    // $#Edited: 16 Jul 96 08:12 $kahuna
    // $#Edited: 17 Jul 96 00:05 $kahuna
    // $#Edited: 17 Jul 96 00:14 $jenner
};

public method .vars() {
    arg this;
    
    return this[2];
    
    // $#Edited: 16 Jul 96 08:12 $kahuna
    // $#Edited: 17 Jul 96 00:05 $kahuna
    // $#Edited: 17 Jul 96 00:14 $jenner
};

public method ._ctext() {
    arg this;
    
    return this[1];
    
    // $#Edited: 16 Jul 96 08:12 $kahuna
    // $#Edited: 17 Jul 96 00:05 $kahuna
    // $#Edited: 17 Jul 96 00:14 $jenner
};

public method .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]>);
    
    // $# Edited 26 Oct 1995 00:35 Lynx ($lynx)
    // $#Edited: 16 Jul 96 08:12 $kahuna
    // $#Edited: 17 Jul 96 00:05 $kahuna
    // $#Edited: 17 Jul 96 00:14 $jenner
};

public method .uncompile() {
    arg this;
    var vars;
    
    vars = this[2];
    if (!(| vars['uncompiler] |))
        vars = vars.add('uncompiler, $j_uncompiler);
    vars = vars.union((vars['uncompiler]).init());
    return ((vars['uncompiler])._eval_ctext(vars, this[1]))[1];
    
    // $#Edited: 16 Jul 96 08:12 $kahuna
    // $#Edited: 16 Jul 96 10:37 $kahuna
    // $#Edited: 16 Jul 96 11:11 $kahuna
    // $#Edited: 17 Jul 96 00:05 $kahuna
    // $#Edited: 17 Jul 96 00:14 $jenner
};

public method .append() {
    arg this, new, [br];
    var data, tmp;
    
    data = this[1];
    if (br) {
        switch (br[1]) {
            case "p":
                data = [@data, (<$ctext_generator, ["p", [], [], 'do_p]>)];
            case "br":
                data = [@data, (<$ctext_generator, ["br", [], [], 'do_br]>)];
            default:
                data = [@data, $ctext_generator.new(br[1], [], [])];
        }
    }
    if (type(new) == 'list)
        data = [@data, @new];
    else if ((type(new) == 'frob) && (class(new) == $j_ctext_frob))
        data = [@data, @new._ctext()];
    else
        data = [@data, new];
    return (<this(), [data, this[2]]>);
    
    // $#Edited: 16 Jul 96 08:12 $kahuna
    // $#Edited: 17 Jul 96 00:05 $kahuna
    // $#Edited: 17 Jul 96 00:14 $jenner
};


new object $j_tag: $frob;

var $root manager = $j_tag;
var $root flags = ['variables, 'methods, 'code, 'core];
var $root created_on = 837584128;
var $root owners = [$j_tag];
var $root child_index = 2;
var $root inited = 1;
var $root managed = [$j_tag];
var $root owned = [$j_tag];

public method .new() {
    arg name, flags, args, method;
    var item, eflags;
    
    eflags = [];
    for item in (flags) {
        switch (type(item)) {
            case 'string:
                eflags = eflags.addkey(item, 1);
            case 'list:
                if (type(item[1]) != 'string)
                    throw(~flagerr, "Flag name must be a string.");
                eflags = eflags.addkey(item[1], item[2]);
            default:
                throw(~flagerr, "Flag must be a string or key,value pair.");
        }
    }
    return (<this(), [name, eflags, args, method]>);
    
    // $#Edited: 06 Jul 96 06:52 Miro ($kahuna)
    // $#Edited: 13 Jul 96 17:12 Miro ($kahuna)
    // $#Edited: 16 Jul 96 02:50 $kahuna
    // $#Edited: 17 Jul 96 00:05 $kahuna
    // $#Edited: 17 Jul 96 00:15 $jenner
};

public method .name() {
    arg self;
    
    return self[1];
    
    // $#Edited: 13 Jul 96 17:12 Miro ($kahuna)
    // $#Edited: 17 Jul 96 00:05 $kahuna
    // $#Edited: 17 Jul 96 00:15 $jenner
};

public method .add_ctext_flag() {
    arg self, key, value;
    var efalgs, uflags;
    
    eflags = (self[2]).addkey(key, value);
    self = self.replace(2, eflags);
    return (<this(), self>);
    
    // $#Edited: 13 Jul 96 17:12 Miro ($kahuna)
    // $#Edited: 17 Jul 96 00:05 $kahuna
    // $#Edited: 17 Jul 96 00:15 $jenner
};

public method .args() {
    arg self;
    
    return self[3];
    
    // $#Edited: 13 Jul 96 17:12 Miro ($kahuna)
    // $#Edited: 17 Jul 96 00:05 $kahuna
    // $#Edited: 17 Jul 96 00:15 $jenner
};

public method .set_args() {
    arg self, args;
    
    return (<this(), self.replace(3, args)>);
    
    // $#Edited: 13 Jul 96 17:12 Miro ($kahuna)
    // $#Edited: 17 Jul 96 00:05 $kahuna
    // $#Edited: 17 Jul 96 00:15 $jenner
};

public method .append_arg() {
    arg self, new;
    var args;
    
    args = [@self[3], new];
    return (<this(), self.replace(3, args)>);
    
    // $#Edited: 13 Jul 96 17:12 Miro ($kahuna)
    // $#Edited: 17 Jul 96 00:05 $kahuna
    // $#Edited: 17 Jul 96 00:15 $jenner
};

public method .method() {
    arg self;
    
    return self[4];
    
    // $#Edited: 13 Jul 96 17:12 Miro ($kahuna)
    // $#Edited: 17 Jul 96 00:05 $kahuna
    // $#Edited: 17 Jul 96 00:15 $jenner
};

public method .ctext_flags() {
    arg self;
    
    return self[2];
    
    // $#Edited: 13 Jul 96 17:12 Miro ($kahuna)
    // $#Edited: 17 Jul 96 00:05 $kahuna
    // $#Edited: 17 Jul 96 00:15 $jenner
};

public method .eval_flags() {
    arg this, vars;
    var flags, i, l, val, ret, s;
    
    flags = this[2];
    l = [];
    s = sender();
    for i in (flags) {
        if (type(i[2]) == 'frob) {
            ret = s._eval_ctext(vars, i[2]);
            l = [@l, [i[1], ret[1]]];
            vars = ret[2];
        } else {
            l = [@l, i];
        }
    }
    return [this[1], l, this[3], this[4]];
    
    // $#Edited: 24 Jul 96 19:30 $jenner
};


new object $j_format: $j_tag;

var $root manager = $j_format;
var $root flags = ['variables, 'methods, 'code, 'core];
var $root created_on = 837584154;
var $root owners = [$j_format];
var $root inited = 1;
var $root managed = [$j_format];
var $root owned = [$j_format];

public method .new() {
    arg name, flags, args;
    
    return pass(name, flags, args, tosym("do_" + (name.strip())));
    
    // $#Edited: 18 Jul 96 21:06 $jenner
};


new object $j_generator: $j_tag;

var $root manager = $j_generator;
var $root flags = ['variables, 'methods, 'code, 'core];
var $root created_on = 837584238;
var $root owners = [$j_generator];
var $root inited = 1;
var $root managed = [$j_generator];
var $root owned = [$j_generator];

public method .new() {
    arg name, flags, args;
    
    return pass(name, flags, args, tosym("gen_" + (name.strip())));
    
    // $#Edited: 18 Jul 96 21:06 $jenner
};


new object $climate_frob: $frob;

var $root manager = $climate_frob;
var $root flags = ['variables, 'methods, 'code, 'core];
var $root created_on = 837904716;
var $root owners = [$climate_frob];
var $root inited = 1;
var $root managed = [$climate_frob];
var $root owned = [$climate_frob];

public method .new() {
    arg attributes, seasons;
    
    return (<this(), [attributes, seasons, #[]]>);
    
    // $#Edited: 20 Jul 96 18:47 $jenner
};

public method .add_weather() {
    arg self, name, attrs, probs, message, ch_messages;
    
    return (<this(), [self[1], self[2], (self[3]).add(name, [attrs, probs, message, ch_messages])]>);
    
    // $#Edited: 20 Jul 96 18:47 $jenner
};

public method .show() {
    arg self;
    var out, seasons, attrs, weathers, x, i;
    
    attrs = self[1];
    seasons = self[2];
    weathers = self[3];
    out = [("Attributes: " + (attrs.to_english())) + ".", ("Seasons: " + (seasons.to_english())) + "."];
    for x in (weathers) {
        out += [((((" * " + (x[1])) + "> (probs) ") + (map i in [1 .. seasons.length()] to ((tostr(seasons[i]) + ":") + (((x[2])[2])[i])).join(", "))) + " (attrs) ") + (map i in [1 .. attrs.length()] to ((tostr(attrs[i]) + ":") + (((x[2])[1])[i])).join(", "))];
        out += ["   Description = " + ((((x[2])[3]).uncompile()).join())];
        if ((x[2])[4]) {
            out += ["   Change messages:"];
            for i in ((x[2])[4])
                out += ["      " + ((i.uncompile()).join())];
        }
    }
    return out;
    
    // $#Edited: 20 Jul 96 18:47 $jenner
};

public method .read_new() {
    arg user;
    var attrs, seasons, i;
    
    attrs = user.prompt("Weather attribute names (default:visibility severity):");
    if (attrs == "@abort")
        throw(~abort, "User aborted.");
    if (type(attrs) == 'symbol)
        throw(~engaged, "Already reading.");
    attrs = attrs || "visibility severity";
    attrs = (attrs.explode()).mmap('to_symbol);
    seasons = user.prompt("Season list (default:winter spring summer fall):");
    if (seasons == "@abort")
        throw(~abort, "User @aborted.");
    if (type(seasons) == 'symbol)
        throw(~engaged, "Already reading.");
    seasons = seasons || "winter spring summer fall";
    seasons = (seasons.explode()).mmap('to_symbol);
    return .new(attrs, seasons);
    
    // $#Edited: 20 Jul 96 18:47 $jenner
};

public method .del_weather() {
    arg self, weather;
    
    return (<this(), [self[1], self[2], (self[3]).del(weather)]>);
    
    // $#Edited: 20 Jul 96 18:47 $jenner
};

public method .read_weather() {
    arg self, user, weather;
    var attrs, probs, message, ch_messages, i;
    
    attrs = user.prompt("Attributes:");
    if ((type(attrs) == 'symbol) || (attrs == "@abort"))
        throw(~stop, "Stopped.");
    if (((self[1]).length()) != ((attrs = attrs.explode_quoted()).length()))
        throw(~parse, "Wrong number of attributes");
    attrs = map i in (attrs) to ((tostr(toint(i)) == i) ? toint(i) : i);
    probs = user.prompt("Seasonal probabilities [0-99]:");
    if ((type(probs) == 'symbol) || (probs == "@abort"))
        throw(~stop, "Stopped.");
    if (((self[2]).length()) != ((probs = probs.explode()).length()))
        throw(~parse, "Wrong number of attributes");
    probs = map i in (probs) to (toint(i));
    message = user.prompt("Weather description:");
    if ((type(message) == 'symbol) || (message == "@abort"))
        throw(~stop, "Stopped.");
    (> (message = $j_compiler.compile_cml(message)) <);
    ch_messages = user.read("Enter messages to be displayed during the change ('.' to finish)");
    if (type(ch_messages) == 'symbol)
        throw(~stop, "Stopped.");
    (> (ch_messages = map i in (ch_messages) to ($j_compiler.compile_cml(i))) <);
    return .add_weather(self, weather, attrs, probs, message, ch_messages);
    
    // $#Edited: 20 Jul 96 18:47 $jenner
};

public method ._distances() {
    arg self, from, season;
    var i, j, attr, w, refattr;
    
    w = self[3];
    refattr = (w[from])[1];
    return map i in (w.keys()) to ([i, ((map j in [1 .. (attr = (w[i])[1]).length()] to (abs((attr[j]) - (refattr[j]))).sum()) * 100) / (((w[i])[2])[season])]);
    
    // $#Edited: 30 Jul 96 21:57 $jenner
};

public method .advance() {
    arg self, current, season, fuzz;
    var dists, i, w, s;
    
    w = self[3];
    dists = ._distances(self, current, season);
    dists = dists.msort(dists.slice(2));
    i = 1;
    while ((i < (dists.length())) && (random(20 + ((dists[i])[2])) < fuzz))
        i++;
    return (dists[i])[1];
    
    // $#Edited: 30 Jul 96 22:15 $jenner
};


new object $housekeeper: $utilities, $system_object;

var $root inited = 1;
var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'variables, 'core];
var $root manager = $housekeeper;
var $root managed = [$housekeeper];
var $root owners = [$housekeeper];
var $root owned = [$housekeeper];

public method .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 method ._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 method .move_user_home() {
    arg who;
    var home, curloc;
    
    if (who.connected())
        return;
    (| who.cleanup_sessions() |);
    curloc = who.location();
    home = who.home();
    if (curloc == home)
        return;
    (| who.move_to(home) |) || (> who.move_to($body_cave) <);
    curloc.did_housekeep(who);
};


new object $has_hooks: $utilities;

var $root child_index = 1;
var $root fertile = 1;
var $root manager = $has_hooks;
var $root owners = [$has_hooks];
var $root created_on = 809174672;
var $root inited = 1;
var $root flags = ['methods, 'code, 'fertile, 'variables, 'core];
var $has_hooks hooks = #[];
var $root managed = [$has_hooks];
var $root owned = [$has_hooks];

public method .hooks() {
    return hooks.keys();
};

public method .add_hook() {
    arg hook;
    
    (> .perms(sender(), 'writer) <);
    hooks = hooks.add(hook, []);
};

public method .del_hook() {
    arg hook;
    
    (> .perms(sender(), 'writer) <);
    hooks = hooks.del(hook);
};

public method .set_hook() {
    arg hook;
    var ret;
    
    if (sender() in (hooks[hook]))
        return 1;
    catch ~methodnf
        ret = .(tosym(("accept_" + tostr(hook)) + "_hook"))(sender());
    with
        ret = 1;
    if (!ret)
        throw(~perm, "Hook not allowed.");
    hooks = hooks.add(hook, [@hooks[hook], sender()]);
    (| .(tosym(("record_" + tostr(hook)) + "_hook"))(sender()) |);
};

public method .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 method .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 method .remove_hook() {
    arg hook;
    
    (> .perms(sender(), 'writers) <);
    hooks = hooks.add(hook, (hooks[hook]).delete(sender() in (hooks[hook])));
};

public method .init_has_hooks() {
    hooks = #[];
};


new object $lag_watcher: $utilities;

var $root manager = $lag_watcher;
var $root inited = 1;
var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'variables, 'core];
var $lag_watcher lags = [0, 1, 0];
var $lag_watcher last_time = 843407445;
var $root managed = [$lag_watcher];
var $root owners = [$lag_watcher];
var $root owned = [$lag_watcher];

public method .startup() {
    // Called by $sys.startup()
    (> .perms(sender(), 'manager) <);
    lags = [];
    last_time = time();
    $heart.add_heartbeat(14);
};

public method .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 method .lag_str() {
    return ("Current server lag is " + (.lag())) + " seconds.";
};

public method .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 method .verbose_lag_str() {
    // returns lag as a string with verbosity
    return [("Current server lag is " + (.lag())) + " seconds.", toliteral(lags)];
    
    // $#Edited: 12 Jun 96 20:00 $levi
};

public method .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 method .pulse() {
    var lag;
    
    // Called by $heart every 15 mins
    (> .perms(sender(), $heart) <);
    lag = time() - last_time;
    last_time = time();
    .update(lag);
};


new object $channels: $utilities;

var $root inited = 1;
var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'variables, 'core];
var $root manager = $channels;
var $root managed = [$channels];
var $root owners = [$channels];
var $root owned = [$channels];

public method .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.ptell(line, #[['type, 'channel], ['channel, channel], ['args, args]]);
    }
};

public method .listeners() {
    arg channel;
    
    switch (channel) {
        case 'admin:
            return $admin.children();
        default:
            return $user_db.connected();
    }
};

public method .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;
};


new object $generics: $utilities;

var $root manager = $generics;
var $root inited = 1;
var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'variables, 'core];
var $generics generics = [$place, $note, $log, #679, $thing, $misc, $frob, $exit];
var $root managed = [$generics];
var $root owners = [$generics];
var $root owned = [$generics];


new object $places: $utilities;

var $root inited = 1;
var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'variables, '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 = [#365, $realm_of_creation, #592, #334];
var $places build_hints = #[[1, <#119, [["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, #626]]]>], [2, <#119, [["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, #626]]]>], [3, <#119, [["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, #626]]]>], [4, <#119, [["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, #626]]]>], [5, <#119, [["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, #626]]]>]];
var $root manager = $places;
var $root managed = [$places];
var $root owners = [$places];
var $root owned = [$places];

public method .place() {
    arg which;
    
    which = tosym(tostr(which) + "_place");
    return (> get_var(which) <);
};

public method .is_place() {
    arg obj;
    
    if (!(obj.has_ancestor($place)))
        throw(~place, ("Object \"" + (obj.namef('ref))) + "\" is not a place.");
};

public method .set_place() {
    arg which, obj;
    
    .perms(sender(), 'manager);
    (> .is_place(obj) <);
    if (!(which in (.variables())))
        throw(~place, (toliteral(which) + " is not a valid entry try one of: ") + toliteral(.variables()));
    set_var(which, obj);
};

public method .coordinates() {
    arg str;
    var x;
    
    for x in (coordinate_shortcuts) {
        if (str.match_template(x[1]))
            return x[2];
    }
    throw(~coordnf, ("Unable to find coordinate shortcut for \"" + str) + "\".");
};

public method .coordinate_shortcuts() {
    return coordinate_shortcuts;
};

public method .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 method .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 method .known_realms() {
    return known_realms;
};

public method .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 method .add_known_realm() {
    arg obj;
    
    (> .perms(sender()) <);
    known_realms = [@known_realms, obj];
};

public method .del_known_realm() {
    arg obj;
    
    (> .perms(sender()) <);
    known_realms = known_realms.setremove(obj);
};

public method .del_build_hint() {
    arg hint_key;
    
    (> .perms(sender()) <);
    build_hints = build_hints.add(hint_key);
};

public method .build_hint() {
    arg hint;
    
    return build_hints[hint];
};

public method .build_hints() {
    return build_hints;
};

public method .add_build_hint() {
    arg hint_key, hint_text;
    
    (> .perms(sender()) <);
    build_hints = build_hints.add(hint_key, hint_text);
};


new object $motd: $utilities;

var $root inited = 1;
var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'variables, 'core];
var $motd server_name = "the Cold Dark";
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'"];
var $motd server_url = "http://ice.cold.org:1180/";
var $root manager = $motd;
var $root managed = [$motd];
var $root owners = [$motd];
var $root owned = [$motd];

public method .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.mmap($sys.admins(), 'name));
                    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;
    
    // $# Edited 29 Oct 1995 12:55 Lynx ($lynx)
};

public method .set_motd() {
    arg what, value;
    
    .perms(sender());
    if (!(what in (.variables())))
        throw(~motd, (toliteral(what) + " is not a valid motd variable, try one of: ") + toliteral(.variables()));
    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 method .build_html() {
    arg [args];
    
    return [("<head><title>" + server_name) + "</title></head>", "<body bgcolor=\"#000000\" text=\"#bbbbbb\">", "<p align=center><img src=\"http://www.cold.org/images/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()).mmap('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://www.cold.org/Software/Genesis/\">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://www.cold.org/Intro/\"><b>Introduction</b></a> |", "<a href=\"/start.html\"><b>DB Starting Points</b></a>", "</p>", @$http_lib.page_tail()];
    
    // $#Edited: 19 Jul 96 00:10 $levi
};

public method .server_name() {
    return server_name;
};

public method .server_title() {
    return server_title;
};

public method .connect_help() {
    return connect_help;
};

public method .set_connect_help() {
    arg text;
    
    (> .perms(sender(), 'manager) <);
    connect_help = text;
};

public method .server_url() {
    return server_url;
    
    // $#Edited: 20 Jul 96 16:44 $jenner
};


new object $ccp: $utilities;

var $root manager = $ccp;
var $root owners = [$ccp];
var $root created_on = 808901418;
var $root inited = 1;
var $root flags = ['methods, 'code, 'variables, 'core];
var $root managed = [$ccp];
var $root owned = [$ccp];


new object $heart: $utilities;

var $root inited = 1;
var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'variables, 'core];
var $heart heart_failures = [];
var $heart hearts = [[#453, 843407445, 60], [$lag_watcher, 843407445, 14]];
var $root manager = $heart;
var $root managed = [$heart];
var $root owners = [$heart];
var $root owned = [$heart];

public method .pulse() {
    var robot, h, x;
    
    // send every robot that has a heart a pulse, if needed.
    for x in [1 .. hearts.length()] {
        robot = hearts[x];
        if (((robot[2]) + (robot[3])) < time()) {
            hearts = hearts.replace(x, [robot[1], time(), robot[3]]);
            (| (robot[1]).pulse() |);
        }
    }
    
    // $#Edited: 11 Feb 96 19:59 Lynx ($lynx)
};

public method .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 method .del_heartbeat() {
    var h, pos;
    
    h = [];
    for pos in [1 .. hearts.length()] {
        if (((hearts[pos])[1]) == sender()) {
            if (pos > 1)
                h = [@h, 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.");
    
    // $#Edited: 15 Nov 95 21:28 Lynx ($lynx)
};


new object $scheduler: $utilities;

var $root owners = [$scheduler];
var $root created_on = 810294797;
var $root inited = 1;
var $root flags = ['methods, 'code, 'variables, 'core];
var $scheduler task_index = 25;
var $scheduler blocked_tasks = #[];
var $scheduler task_queue = [[15, 300, 843406765, #997, $connection, 'do_timeout, ['system], []], [16, 300, 843406770, #999, $connection, 'do_timeout, ['system], []], [17, 300, 843406894, #1001, $connection, 'do_timeout, ['system], []], [19, 300, 843406968, $housekeeper, $housekeeper, 'move_user_home, ['system], [#2]], [18, 300, 843406968, $housekeeper, $housekeeper, 'move_user_home, ['system], [#1005]], [20, 300, 843407022, #993, $connection, 'do_timeout, ['system], []], [21, 300, 843407119, $housekeeper, $housekeeper, 'move_user_home, ['system], [#999]], [22, 300, 843407189, #993, $connection, 'do_timeout, ['system], []], [23, 300, 843407405, $housekeeper, $housekeeper, 'move_user_home, ['system], [#999]], [24, 300, 843407418, #993, $connection, 'do_timeout, ['system], []], [25, 300, 843407455, $housekeeper, $housekeeper, 'move_user_home, ['system], [#1001]]];
var $scheduler sub_schedulers = [$heart];
var $scheduler expected_lag = 0;
var $scheduler server_lag = 0;
var $root manager = $scheduler;
var $scheduler suspended_tasks = #[];
var $root managed = [$scheduler];
var $root owned = [$scheduler];

public method .sleep() {
    arg howlong;
    
    .add_task(howlong, 'resume_job, task_id());
    $sys.suspend();
};

public method .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 {
        // $sys.log(traceback());
    }
    if (!task_queue)
        task_index = 1;
};

public method .task_queue() {
    // sender must be system, for now
    (> .perms(sender(), 'system) <);
    return task_queue;
};

public method .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 method .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 method .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 method ._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, toliteral(args[a]));
            args = $list.join(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 {
        // 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: " + toliteral(task));
        // }
        //  }
    }
    
    // $#Edited: 12 Jun 96 20:01 $levi
    // $#Edited: 18 Jul 96 14:56 $levi
};

public method .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 method .add_sub_scheduler() {
    arg object;
    
    if (!($sys.is_admin(sender())))
        throw(~perm, "Only admins may add sub schedulers.");
    if (type(object) != 'objnum)
        throw(~type, "Object must be a dbref.");
    sub_schedulers = [@sub_schedulers, object];
};

public method .del_sub_scheduler() {
    arg object;
    var pos, s;
    
    if (!($sys.is_admin(sender())))
        throw(~perm, "Only admins may delete sub schedulers.");
    if (type(object) != 'objnum)
        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 method .has_blocked_tasks() {
    arg ident;
    
    return blocked_tasks.contains(ident);
};

public method .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 method .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 method .resume_job() {
    arg tid;
    
    $sys.resume(tid);
};

public method .suspend() {
    arg [objs];
    
    objs += [user()];
    suspended_tasks = dict_add(suspended_tasks, task_id(), objs);
    return (> suspend() <);
};

public method .resume() {
    arg task_id, [return_value];
    var objs;
    
    if ((objs = (| suspended_tasks[task_id] |))) {
        if ((!(sender() in objs)) && (!($sys.is_system(sender()))))
            throw(~perm, (sender() + " may not resume task ") + task_id);
        suspended_tasks = dict_del(suspended_tasks, task_id);
    }
    return (> resume(task_id, @return_value) <);
};

public method .cancel() {
    arg task_id;
    var objs;
    
    if ((objs = (| suspended_tasks[task_id] |))) {
        if ((!(sender() in objs)) && (!($sys.is_system(sender()))))
            throw(~perm, (sender() + " may not cancel task ") + task_id);
        suspended_tasks = dict_del(suspended_tasks, task_id);
    }
    return (> cancel(task_id) <);
};


new object $veil: $utilities;

var $root manager = $veil;
var $root flags = ['methods, 'code, 'variables, 'core];
var $root created_on = 814666382;
var $root inited = 1;
var $root managed = [$veil];
var $root owners = [$veil];
var $root owned = [$veil];


new object $file: $utilities;

var $root manager = $file;
var $root flags = ['variables, 'methods, 'code, 'core];
var $root created_on = 843324817;
var $root owners = [$file];
var $root inited = 1;
var $root managed = [$file];
var $root owned = [$file];


new object $j_compiler: $utilities;

var $root manager = $j_compiler;
var $root flags = ['variables, 'methods, 'code, 'core];
var $root created_on = 837583518;
var $root owners = [$j_compiler];
var $root inited = 1;
var $root managed = [$j_compiler];
var $root owned = [$j_compiler];

public method .parse_string_new() {
    arg vars, tokens;
    var mode, out, word, token, i, tmp, ret;
    
    if (!tokens)
        return [[], vars];
    mode = ['spaces, 'plain];
    i = 1;
    out = [];
    word = "";
    while (i <= (tokens.length())) {
        refresh();
        token = tokens[i++];
        switch (mode[1]) {
            case 'spaces:
                if (token != " ") {
                    i--;
                    mode = mode.subrange(2);
                }
            case 'plain:
                if (!(token in ["{", "["])) {
                    word += token.sed("\\\(.)", "%1", "g");
                } else {
                    if (word)
                        out = [@out, word];
                    word = "";
                    mode = ['spaces, (token == "{") ? 'fmtname : 'genname, @mode];
                }
            case 'fmtname, 'genname:
                if (((mode[1]) == 'fmtname) && (token == "quote")) {
                    mode = mode.subrange(2);
                    out = [@out, $j_format.new("quote", [], [tokens[i++]])];
                    i++;
                } else {
                    mode = ['spaces, 'flags, #[], token, @mode];
                }
            case 'flags:
                if (token in ["}", "]"]) {
                    ret = ._make_tag(token, mode[4], vars, mode[3], mode[2], []);
                    vars = ret[2];
                    out = [@out, ret[1]];
                    mode = mode.subrange(5);
                } else if (token == "=") {
                    throw(~parse, "Value flag with no key.");
                } else if (token == ":") {
                    mode = ['args, out, mode[2], @mode.subrange(3)];
                    out = [];
                } else if ((| (tokens[i]) == "=" |)) {
                    mode = ['flagvalue, token, @mode];
                    i++;
                } else {
                    mode = mode.replace(2, (mode[2]).add(token, 1));
                }
            case 'flagvalue:
                if (token in ["}", "]", ":"]) {
                    mode = mode.subrange(3);
                    i--;
                } else if (token == " ") {
                    mode = mode.subrange(3);
                } else if (!(token in ["[", "{"])) {
                    mode = ['spaces, @(mode.subrange(3)).replace(2, (mode[4]).add(mode[2], token))];
                } else {
                    if (word)
                        out = [@out, word];
                    word = "";
                    mode = ['spaces, (token == "{") ? 'fmtname : 'genname, 'flagset, @mode.subrange(2)];
                }
            case 'flagset:
                i--;
                mode = ['spaces, @(mode.subrange(3)).replace(2, (mode[4]).add(mode[2], out[out.length()]))];
                out = out.subrange(1, (out.length()) - 1);
            case 'args:
                if (token in ["}", "]"]) {
                    if (word) {
                        out = [@out, word];
                        word = "";
                    }
                    ret = ._make_tag(token, mode[5], vars, mode[4], mode[3], out);
                    vars = ret[2];
                    out = [@mode[2], ret[1]];
                    mode = mode.subrange(6);
                } else if (token in ["{", "["]) {
                    if (word)
                        out = [@out, word];
                    word = "";
                    mode = ['spaces, (token == "{") ? 'fmtname : 'genname, @mode];
                } else {
                    word += token.sed("\\\(.)", "%1", "g");
                }
        }
    }
    if (word)
        out = [@out, word];
    if (mode != ['plain])
        throw(~parse, "Unclosed tag.");
    return [out, vars];
    
    // $#Edited: 30 Jul 96 16:29 $jenner
};

public method .tokenize() {
    arg text;
    var word, out, escaped, token, i, pre_count, open_form, str;
    
    // break text into a list of tokens.
    if (type(text) == 'string)
        text = [text];
    out = [];
    word = "";
    escaped = 0;
    
    // pre_count is 0, except inside pre, when it counts the {}'s
    pre_count = 0;
    open_form = 0;
    for str in (text) {
        str = str.explode(" ", 1);
        for token in (str) {
            refresh();
            if ((!token) && (!pre_count))
                continue;
            while ((i = token.match_regexp("[][{}=\:]"))) {
                i = (i[1])[1];
                if (escaped) {
                    escaped = 0;
                    word = (word + "\\") + (token.subrange(1, i));
                } else if (pre_count) {
                    if ((token[i]) == "{") {
                        pre_count++;
                        word += token.subrange(1, i);
                    } else if ((token[i]) == "}") {
                        pre_count--;
                        if (pre_count) {
                            word += token.subrange(1, i);
                        } else {
                            word = word + (token.subrange(1, i - 1));
                            out = word ? [@out, word, token[i]] : [@out, token[i]];
                            word = "";
                        }
                    } else {
                        word += token.subrange(1, i);
                    }
                } else {
                    word += token.subrange(1, i - 1);
                    open_form = (token[i]) == "{";
                    if ((token[i]) == "\\") {
                        escaped = 1;
                    } else {
                        out = word ? [@out, word, token[i]] : [@out, token[i]];
                        word = "";
                    }
                }
                token = token.subrange(i + 1);
            }
            if (open_form && (token == "quote")) {
                pre_count = 1;
                open_form = 0;
                out = [@out, token];
                token = "";
                continue;
            }
            word = word + token;
            if (escaped || pre_count) {
                escaped = 0;
                word += " ";
            } else {
                out = word ? [@out, word, " "] : [@out, " "];
                word = "";
            }
        }
        if (pre_count)
            word = word ? (word.subrange(1, (word.length()) - 1)) + "\n" : "\n";
    }
    if (word)
        out = [@out, word];
    if (out) {
        if ((out.last()) == " ")
            out = out.subrange(1, (out.length()) - 1);
        else
            out = out.replace(out.length(), (out.last()).subrange(((out.last()).length()) - 1));
    }
    return out;
    
    // $#Edited: 30 Jul 96 16:29 $jenner
};

public method ._make_tag() {
    arg token, mode, vars, name, flags, args;
    var method, class;
    
    if (mode == 'fmtname) {
        if (token != "}")
            throw(~parse, "Extra ']' encountered.");
        method = tosym("do_" + (name.strip()));
        class = $j_format;
    }
    if (mode == 'genname) {
        if (token != "]")
            throw(~parse, "Extra '}' encountered.");
        method = tosym("gen_" + (name.strip()));
        class = $j_generator;
    }
    catch ~methodnf
        return .(method)(vars, flags, args);
    with
        return [class.new(name, flags, args), vars];
    
    // $#Edited: 30 Jul 96 16:05 $jenner
};

public method .compile_cml() {
    arg text;
    var vars, ret;
    
    vars = #[['this, sender()]];
    (> (ret = .parse_string_new(vars, .tokenize(text))) <);
    return $j_ctext_frob.new(ret[1], ret[2]);
    
    // $#Edited: 16 Jul 96 07:02 $kahuna
    // $#Edited: 17 Jul 96 00:05 $kahuna
    // $#Edited: 17 Jul 96 00:06 $jenner
};

public method .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}.");
    if (((args.length()) != 1) && (type(args[1]) != 'string))
        throw(~parse, "{link} argument must be a string");
    links = links.add(args[1], node);
    return [$j_format.new("link", flags, args), vars.add('links, links)];
    
    // $#Edited: 30 Jul 96 17:51 $jenner
};


new object $j_evaluator: $utilities;

var $root manager = $j_evaluator;
var $root flags = ['variables, 'methods, 'code, 'core];
var $root created_on = 837583637;
var $root owners = [$j_evaluator];
var $root child_index = 3;
var $root inited = 1;
var $root managed = [$j_evaluator];
var $root owned = [$j_evaluator];

public method .eval_formatter() {
    arg vars, form;
    var flags, ret, key;
    
    form = form.eval_flags(vars);
    catch ~methodnf {
        return .(form[4])(vars, form[2], form[3]);
    } with {
        ret = ._eval_ctext(vars, form[3]);
        form = (<$j_format, [form[1], form[2], ret[1], form[4]]>);
        vars = ret[2];
        return [[form], vars];
    }
};

public method ._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 $j_generator:
                    ret = .eval_generator(vars, token);
                    out = [@out, @ret[1]];
                    vars = ret[2];
                case $j_format:
                    ret = .eval_formatter(vars, token);
                    out = [@out, @ret[1]];
                    vars = ret[2];
                default:
                    out = [@out, token];
            }
        } else {
            out = [@out, token];
        }
    }
    return [out, vars];
    
    // $#Edited: 16 Jul 96 08:12 $kahuna
    // $#Edited: 16 Jul 96 10:37 $kahuna
    // $#Edited: 17 Jul 96 00:05 $kahuna
    // $#Edited: 17 Jul 96 00:07 $jenner
};

public method .eval_generator() {
    arg vars, gen;
    var flags, key, value, ret, name;
    
    gen = gen.eval_flags(vars);
    catch ~methodnf {
        return .(gen[4])(vars, gen[2], gen[3]);
    } with {
        catch ~keynf
            return [[vars[gen[1]]], vars];
        with
            return [[(">>ERROR: Unknown generator [" + (gen[1])) + "].<<"], vars];
    }
    
    // $#Edited: 24 Jul 96 21:08 $jenner
};

public method .init() {
    return #[];
    
    // $#Edited: 16 Jul 96 08:43 $kahuna
    // $#Edited: 17 Jul 96 00:05 $kahuna
    // $#Edited: 17 Jul 96 00:07 $jenner
};


new object $j_bs_eval: $j_evaluator;

var $root manager = $j_bs_eval;
var $root flags = ['variables, 'methods, 'code, 'core];
var $root created_on = 837583704;
var $root owners = [$j_bs_eval];
var $root inited = 1;
var $root managed = [$j_bs_eval];
var $root owned = [$j_bs_eval];

public method .gen_servername() {
    arg vars, flags, args;
    
    return [[$motd.server_name()], vars];
    
    // $#Edited: 16 Jul 96 08:12 $kahuna
    // $#Edited: 17 Jul 96 00:05 $kahuna
    // $#Edited: 17 Jul 96 00:08 $jenner
};

public method .gen_set() {
    arg vars, flags, args;
    var ret, name;
    
    name = flags.getkey("var");
    ret = ._eval_ctext(vars, [args[1]]);
    return [[""], vars.add(name, (ret[1])[1])];
    
    // $#Edited: 16 Jul 96 08:12 $kahuna
    // $#Edited: 17 Jul 96 00:05 $kahuna
    // $#Edited: 17 Jul 96 00:08 $jenner
};

public method .gen_name() {
    arg vars, flags, args;
    var ret, out, name, sep, type;
    
    ret = ._eval_ctext(vars, args);
    args = ret[1];
    vars = ret[2];
    out = [];
    type = (| tosym(flags.getkey("type")) |) || 'name;
    if (!args)
        return [[(vars['this]).namef(type)], vars];
    sep = (| flags.getkey("separator") |) || " ";
    for name in (args) {
        switch (type(name)) {
            case 'objnum:
                name = name.namef(type);
            case 'string:
                catch ~objnf
                    name = ((vars['this]).match_environment(name)).namef(type);
                with
                    name = name;
        }
        out = [@out, name];
    }
    return [._separate_list(sep, out), vars];
    
    // $#Edited: 16 Jul 96 08:12 $kahuna
    // $#Edited: 17 Jul 96 00:05 $kahuna
    // $#Edited: 17 Jul 96 00:08 $jenner
};

public method ._separate_list() {
    arg sep, l;
    
    if (sep == "none")
        return l;
    if (sep == "english")
        return [l.to_english()];
    return [l.join(sep)];
    
    // $#Edited: 16 Jul 96 08:12 $kahuna
    // $#Edited: 17 Jul 96 00:05 $kahuna
    // $#Edited: 17 Jul 96 00:08 $jenner
    // $#Edited: 18 Jul 96 14:56 $levi
};

public method .gen_english() {
    arg vars, flags, args;
    var ret, sep, empty, and;
    
    sep = ((| flags.getkey("separator") |) || ",") + " ";
    empty = (| flags.getkey("empty") |) || "nothing";
    and = (| flags.getkey("and") |) || " and ";
    ret = ._eval_ctext(vars, args);
    return [[(ret[1]).to_english(empty, and, sep)], ret[2]];
    
    // $#Edited: 16 Jul 96 08:12 $kahuna
    // $#Edited: 17 Jul 96 00:05 $kahuna
    // $#Edited: 17 Jul 96 00:08 $jenner
};

public method .gen_def() {
    arg vars, flags, args;
    var ret, name;
    
    name = flags.getkey("var");
    val = args[1];
    return [[""], vars.add(name, val)];
    
    // $#Edited: 16 Jul 96 08:12 $kahuna
    // $#Edited: 17 Jul 96 00:05 $kahuna
    // $#Edited: 17 Jul 96 00:08 $jenner
};

public method .gen_foreach() {
    arg vars, flags, args;
    var v, list, body, out, item, ret, sep;
    
    v = (| flags.getkey("var") |) || "iterator";
    list = flags.getkey("list");
    sep = (| flags.getkey("separator") |) || " ";
    out = [];
    for item in (list) {
        vars = vars.add(v, item);
        ret = ._eval_ctext(vars, args);
        if ((vars['time]) == 'post)
            out = out.affix(ret[1]);
        else
            out = [@out, @ret[1]];
        vars = ret[2];
    }
    if ((vars['time]) == 'pre)
        out = ._separate_list(sep, out);
    return [out, vars];
    
    // $#Edited: 28 Mar 96 16:39 Jenner ($jenner)
    // $#Edited: 16 Jul 96 08:12 $kahuna
    // $#Edited: 17 Jul 96 00:05 $kahuna
    // $#Edited: 17 Jul 96 00:08 $jenner
};

public method .gen_time() {
    arg vars, flags, args;
    var ret, a, word;
    
    ret = ._eval_ctext(vars, args);
    args = ret[1];
    vars = ret[2];
    if (!args)
        return [tostr(time()), vars];
    return [[$time.format(args[1])], vars];
    
    // $#Edited: 16 Jul 96 08:12 $kahuna
    // $#Edited: 17 Jul 96 00:05 $kahuna
    // $#Edited: 17 Jul 96 00:08 $jenner
};

public method .gen_vars() {
    arg vars, flags, args;
    var out, v, sep;
    
    out = [];
    sep = (| flags.getkey("separator") |) || " ";
    for v in (vars.keys()) {
        if (type(v) == 'string)
            out = [@out, v];
    }
    return [._separate_list(sep, out), vars];
    
    // $#Edited: 16 Jul 96 08:12 $kahuna
    // $#Edited: 17 Jul 96 00:05 $kahuna
    // $#Edited: 17 Jul 96 00:08 $jenner
};

public method .gen_pro() {
    arg vars, flags, args;
    var out, type, obj, ret, key, objstr;
    
    if ((| (objstr = flags.getkey("obj")) |)) {
        while (type(objstr) == 'list)
            objstr = objstr[1];
        if (type(objstr) == 'string) {
            catch ~objnf, ~namenf
                obj = (vars['this]).match_environment(objstr);
            with
                return [[(">>ERROR: Invalid object '" + objstr) + "'.<<"], vars];
        } else {
            obj = objstr;
        }
    } else {
        obj = vars['this];
    }
    out = [];
    if ((args.length()) == 0)
        return [[">>ERROR: Must specify pronoun type.<<"], vars];
    type = (args[1]).to_symbol();
    catch ~keynf
        out = (> [(obj.gender()).pronoun(type)] <);
    with
        return [[(">>ERROR: Invalid pronoun type '" + (args[1])) + "'.<<"], vars];
    return [out, vars];
    
    // $#Edited: 24 Jul 96 19:55 $jenner
};

public method .gen_join() {
    arg vars, flags, args;
    var v, sep, ret;
    
    sep = (| flags.getkey("separator") |) || " ";
    ret = ._eval_ctext(vars, args);
    return [._separate_list(sep, ret[1]), ret[2]];
    
    // $#Edited: 16 Jul 96 08:32 $kahuna
    // $#Edited: 17 Jul 96 00:05 $kahuna
    // $#Edited: 17 Jul 96 00:08 $jenner
};

public method .gen_columnize() {
    arg vars, flags, args;
    var v, cols, ret;
    
    cols = (| flags.getkey("columns") |) || "*";
    ret = ._eval_ctext(vars, args);
    return [[(cols == "*") ? (ret[1]).lcolumnize() : ((ret[1]).columnize(toint(cols)))], ret[2]];
    
    // $#Edited: 16 Jul 96 08:32 $kahuna
    // $#Edited: 17 Jul 96 00:05 $kahuna
    // $#Edited: 17 Jul 96 00:08 $jenner
};

public method .gen_serverurl() {
    arg vars, flags, args;
    
    return [[$motd.server_url()], vars];
    
    // $#Edited: 20 Jul 96 17:17 $jenner
};

public method .gen_server_name() {
    arg vars, flags, args;
    
    return [[$motd.server_name()], vars];
    
    // $#Edited: 16 Jul 96 08:12 $kahuna
    // $#Edited: 17 Jul 96 00:05 $kahuna
    // $#Edited: 17 Jul 96 00:08 $jenner
    // $#Edited: 28 Jul 96 23:23 $jenner
};


new object $j_formatter: $j_evaluator;

var $root manager = $j_formatter;
var $root flags = ['variables, 'methods, 'code, 'core];
var $root created_on = 837583776;
var $root owners = [$j_formatter];
var $root child_index = 2;
var $root inited = 1;
var $root managed = [$j_formatter];
var $root owned = [$j_formatter];

public method .eval_generator() {
    arg vars, gen;
    
    return (vars['evaluator]).eval_generator(vars, gen);
    
    // $#Edited: 16 Jul 96 10:37 $kahuna
    // $#Edited: 17 Jul 96 00:05 $kahuna
    // $#Edited: 17 Jul 96 00:09 $jenner
};

public method ._eval_ctext() {
    arg vars, data;
    var out, uflags, ret, token;
    
    out = "";
    if (type(data) != 'list)
        data = [data];
    for token in (data) {
        refresh();
        switch (type(token)) {
            case 'frob:
                switch (class(token)) {
                    case $j_generator:
                        ret = ((| vars['evaluator] |) || $j_bs_eval).eval_generator(vars, token);
                        token = ret[1];
                        vars = ret[2];
                        if (type(token) == 'string) {
                            out += token;
                        } else {
                            ret = ._eval_ctext(vars, token);
                            out += ret[1];
                            vars = ret[2];
                        }
                    case $j_format:
                        ret = .eval_formatter(vars, token);
                        token = ret[1];
                        vars = ret[2];
                        out += token;
                }
            case 'string:
                out += token;
            case 'list:
                ret = ._eval_ctext(vars, token);
                token = ret[1];
                out = out + token;
            default:
                out = out + token;
        }
    }
    return [out, vars];
    
    // $#Edited: 24 Jul 96 21:12 $jenner
};

public method .eval_formatter() {
    arg vars, form;
    var flags, ret, key;
    
    form = form.eval_flags(vars);
    catch ~methodnf {
        return .(form[4])(vars, form[2], form[3]);
    } with {
        ret = ._eval_ctext(vars, form[3]);
        if ((vars['time]) == 'post)
            return ret;
        form = (<$j_format, [form[1], form[2], ret[1], form[4]]>);
        vars = ret[2];
        return [[form], vars];
    }
    
    // $#Edited: 18 Jul 96 15:59 $jenner
};


new object $j_telnet_format: $j_formatter;

var $root manager = $j_telnet_format;
var $root flags = ['variables, 'methods, 'code, 'core];
var $root created_on = 837583858;
var $root owners = [$j_telnet_format];
var $root inited = 1;
var $root managed = [$j_telnet_format];
var $root owned = [$j_telnet_format];

public method .init() {
    return #[['header, ""], ['width, 78]];
    
    // $#Edited: 17 Jul 96 04:00 $kahuna
    // $#Edited: 17 Jul 96 00:11 $jenner
};

public method .do_ul() {
    arg vars, flags, args;
    var ret, out, token;
    
    ret = ._eval_ctext(vars, args);
    vars = ret[2];
    return [("\n\n" + (ret[1])) + "\n", vars];
    
    // $#Edited: 30 Jul 96 17:23 $jenner
};

public method .do_strong() {
    arg vars, flags, args;
    var out, ret, a;
    
    ret = ._eval_ctext(vars, args);
    return [ret[1], ret[2]];
    
    // $#Edited: 01 Aug 96 14:59 $jenner
};

public method .do_dl() {
    arg vars, flags, args;
    var out, ret, token, c;
    
    c = (| vars['compact] |) || 0;
    vars = vars.add('compact, (| flags.getkey("compact") |) || 0);
    ret = ._eval_ctext(vars, args);
    vars = (ret[2]).add('compact, c);
    return [(ret[1]) + "\n", vars];
    
    // $#Edited: 17 Jul 96 04:00 $kahuna
    // $#Edited: 17 Jul 96 00:11 $jenner
};

public method .do_anchor() {
    arg vars, flags, args;
    var ret;
    
    ret = ._eval_ctext(vars, args[1]);
    return [("/" + (ret[1])) + "/", ret[2]];
    
    // $#Edited: 16 Jul 96 10:37 $kahuna
    // $#Edited: 17 Jul 96 00:05 $kahuna
    // $#Edited: 17 Jul 96 00:11 $jenner
};

public method .do_p() {
    arg vars, args, flags;
    
    return ["\n", vars];
    
    // $#Edited: 17 Jul 96 00:05 $kahuna
    // $#Edited: 17 Jul 96 04:00 $kahuna
    // $#Edited: 17 Jul 96 00:11 $jenner
};

public method .do_em() {
    arg vars, flags, args;
    var out, ret, a;
    
    ret = ._eval_ctext(vars, args);
    return [ret[1], ret[2]];
    
    // $#Edited: 01 Aug 96 14:59 $jenner
};

public method .do_br() {
    arg vars, args, flags;
    
    return ["\n", vars];
    
    // $#Edited: 17 Jul 96 00:05 $kahuna
    // $#Edited: 17 Jul 96 04:00 $kahuna
    // $#Edited: 17 Jul 96 00:11 $jenner
};

public method .do_bold() {
    arg vars, flags, args;
    var out, ret, a;
    
    ret = ._eval_ctext(vars, args);
    return [(ret[1]).uppercase(), ret[2]];
    
    // $#Edited: 16 Jul 96 10:37 $kahuna
    // $#Edited: 17 Jul 96 00:05 $kahuna
    // $#Edited: 17 Jul 96 00:11 $jenner
};

public method .do_link() {
    arg vars, flags, args;
    var ret;
    
    ret = ._eval_ctext(vars, args[1]);
    return [("/" + (ret[1])) + "/", ret[2]];
    
    // $#Edited: 16 Jul 96 10:37 $kahuna
    // $#Edited: 16 Jul 96 11:27 $kahuna
    // $#Edited: 17 Jul 96 00:05 $kahuna
    // $#Edited: 17 Jul 96 00:11 $jenner
};

public method .do_web() {
    arg vars, flags, args;
    var ret;
    
    ret = ._eval_ctext(vars, args);
    return [(((((ret[1]) + "<") + (flags.getkey("name"))) + ":") + (flags.getkey("src"))) + ">", ret[2]];
    
    // $#Edited: 16 Jul 96 10:37 $kahuna
    // $#Edited: 17 Jul 96 00:05 $kahuna
    // $#Edited: 17 Jul 96 00:11 $jenner
};

public method .do_hr() {
    arg vars, flags, args;
    
    return [("\n" + ("-".repeat(vars['width]))) + "\n", vars];
    
    // $#Edited: 17 Jul 96 07:59 $kahuna
    // $#Edited: 17 Jul 96 00:11 $jenner
};

public method .do_tt() {
    arg vars, flags, args;
    var out, ret, a;
    
    ret = ._eval_ctext(vars, args);
    args = ret[1];
    vars = ret[2];
    return [("`" + args) + "`", vars];
    
    // $#Edited: 16 Jul 96 10:37 $kahuna
    // $#Edited: 17 Jul 96 00:05 $kahuna
    // $#Edited: 17 Jul 96 00:11 $jenner
};

public method .do_action() {
    arg vars, flags, args;
    var ret;
    
    ret = ._eval_ctext(vars, args[1]);
    (| (vars['receiver]).register_action(ret[1], vars['this], flags.getkey("token")) |);
    return [("\\" + (ret[1])) + "\\", ret[2]];
    
    // $#Edited: 16 Jul 96 10:37 $kahuna
    // $#Edited: 17 Jul 96 00:05 $kahuna
    // $#Edited: 17 Jul 96 00:11 $jenner
};

public method .do_subj() {
    arg vars, flags, args;
    var out, ret, word, l;
    
    ret = ._eval_ctext(vars, args);
    out = ret[1];
    switch (toint((| flags.getkey("level") |) || "4")) {
        case 1:
            out = (((" " + out) + " ").center(vars['width], "-")) + "\n\n";
        case 2:
            out = ((("\n" + (out.center(vars['width]))) + "\n") + (("".pad(out.length(), "=")).center(vars['width]))) + "\n\n";
        case 3:
            out = ((("\n\n" + out) + "\n") + ("".pad(out.length(), "-"))) + "\n";
        default:
            out = ("\n" + out) + "\n";
    }
    return [out, ret[2]];
    
    // $#Edited: 30 Jul 96 17:20 $jenner
};

public method .do_dd() {
    arg vars, flags, args;
    var ret;
    
    ret = .eval_indented(vars, args, 4);
    if ((| vars['compact] |))
        return [ret[1], ret[2]];
    else
        return [(ret[1]) + "\n", vars];
    
    // $#Edited: 17 Jul 96 04:00 $kahuna
    // $#Edited: 17 Jul 96 00:11 $jenner
};

public method .do_b() {
    arg vars, flags, args;
    var out, ret, a;
    
    ret = ._eval_ctext(vars, args);
    return [ret[1], ret[2]];
    
    // $#Edited: 01 Aug 96 14:59 $jenner
};

public method .do_i() {
    arg vars, flags, args;
    var out, ret, a;
    
    ret = ._eval_ctext(vars, args);
    return [ret[1], ret[2]];
    
    // $#Edited: 01 Aug 96 14:59 $jenner
};

public method .do_dt() {
    arg vars, flags, args;
    var ret;
    
    vars = vars.add('header, "");
    ret = ._eval_ctext(vars, args);
    if (vars['compact])
        return ["\n  ", (ret[2]).add('header, ("  " + (ret[1])).pad(25))];
    else
        return [("\n  " + (ret[1])) + "\n", (ret[2]).add('header, "")];
    
    // $#Edited: 17 Jul 96 04:00 $kahuna
    // $#Edited: 17 Jul 96 00:11 $jenner
};

public method .do_li() {
    arg vars, flags, args;
    var line, ret;
    
    vars = vars.add('header, "* ");
    ret = .eval_indented(vars, args, 2);
    return [(ret[1]) + "\n", ret[2]];
    
    // $#Edited: 17 Jul 96 04:00 $kahuna
    // $#Edited: 17 Jul 96 00:11 $jenner
};

public method .do_lh() {
    arg vars, flags, args;
    var line, ret;
    
    vars = vars.add('header, "");
    ret = .eval_indented(vars, args, 2);
    return [(ret[1]) + "\n", ret[2]];
    
    // $#Edited: 17 Jul 96 04:00 $kahuna
    // $#Edited: 17 Jul 96 00:11 $jenner
};

public method .do_quote() {
    arg vars, flags, args;
    
    return [args[1], vars];
    
    // $#Edited: 17 Jul 96 01:20 $kahuna
    // $#Edited: 17 Jul 96 00:11 $jenner
};

public method .do_table() {
    arg vars, flags, args;
    var tblinfo, i, ntbinfo, info, ret, width, l, lr, lc, inds;
    
    tblinfo = (| vars['table_info] |) || 0;
    width = vars['width];
    if (!(| (ntbinfo = flags.getkey("columns")) |))
        throw(~flags, "Need columns information.");
    info = [];
    for i in (ntbinfo.explode(","))
        info = [@info, toint(i)];
    vars = ((vars.add('table_info, [info, 0])).add('rinfo, $list.make(info.length(), 0))).add('rcont, $list.make(info.length(), []));
    ret = ._eval_ctext(vars, args);
    vars = tblinfo ? (ret[2]).add('table_info, tblinfo) : ((ret[2]).del('table_info));
    vars = vars.add('width, width);
    return ["\n" + (ret[1]), vars];
    
    // $#Edited: 17 Jul 96 04:00 $kahuna
    // $#Edited: 17 Jul 96 07:59 $kahuna
    // $#Edited: 17 Jul 96 00:11 $jenner
};

public method .do_tr() {
    arg vars, flags, args;
    var tblinfo, rinfo, i, l, ret, len, s, j, col, rcont, done;
    
    tblinfo = vars['table_info];
    tblinfo = tblinfo.replace(2, 0);
    rinfo = vars['rinfo];
    rcont = vars['rcont];
    vars = vars.add('table_info, tblinfo);
    l = [];
    col = 1;
    for i in (args) {
        if (((!type(i)) == 'frob) || ((!(i.method())) == 'do_td))
            throw(~table, "Only {td} tags permitted in rows");
        ret = .do_td(vars, i.ctext_flags(), i.args());
        l = [@l, @(i = ret[1])];
        vars = ret[2];
        for j in (i) {
            rcont = rcont.replace(col, [@rcont[col], @j[3]]);
            col++;
        }
    }
    l = [@l, @$list.make((rcont.length()) - (l.length()), [0, 0, []])];
    s = "";
    i = 1;
    done = 0;
    for j in [1 .. rinfo.length()]
        rinfo = rinfo.replace(j, ((rinfo[j]) - 1) + ((l[j])[1]));
    while (!done) {
        done = 1;
        for j in [1 .. rcont.length()] {
            s += ((| (rcont[j])[i] |) || "").pad((l[j])[2]);
            done = done && ((rinfo[j]) || (i > ((rcont[j]).length())));
        }
        i++;
        s += "\n";
    }
    for j in [1 .. rinfo.length()]
        rcont = rcont.replace(j, (| (rcont[j]).subrange(i) |) || []);
    vars = (vars.add('rinfo, rinfo)).add('rcont, rcont);
    return [s, vars];
    
    // $#Edited: 17 Jul 96 04:00 $kahuna
    // $#Edited: 17 Jul 96 07:59 $kahuna
    // $#Edited: 17 Jul 96 00:11 $jenner
};

public method .do_td() {
    arg vars, flags, args;
    var tblinfo, rinfo, col, ret, cols, rows, w, n;
    
    rows = (| toint(flags.getkey("rowspan")) |) || 1;
    cols = (| toint(flags.getkey("colspan")) |) || 1;
    rinfo = vars['rinfo];
    tblinfo = vars['table_info];
    col = (tblinfo[2]) + 1;
    n = [];
    while (rinfo[col]) {
        col++;
        n += [[0, (tblinfo[1])[col], []]];
    }
    w = ((tblinfo[1]).subrange(col, cols)).sum();
    tblinfo = tblinfo.replace(2, (col + cols) - 1);
    vars = (vars.add('table_info, tblinfo)).add('header, "");
    ret = .eval_indented(vars, args, 0, w);
    return [(n + [[rows, w, (ret[1]).explode("\n", 1)]]) + ($list.make(cols - 1, [rows, 0, []])), ret[2]];
    
    // $#Edited: 17 Jul 96 07:59 $kahuna
    // $#Edited: 17 Jul 96 00:11 $jenner
};

public method .rewrap_lines() {
    arg vars, str, prefix;
    var s, p, n;
    
    s = [];
    n = (vars['width]) + (prefix.length());
    for str in (str.explode("\n", 1))
        s = [@s, @str.wrap_lines(n, prefix, 1)];
    return s.join("\n");
    
    // $#Edited: 17 Jul 96 04:00 $kahuna
    // $#Edited: 17 Jul 96 00:11 $jenner
    // $#Edited: 18 Jul 96 14:56 $levi
};

public method .eval_indented() {
    arg vars, args, i, [w];
    var width, indent;
    
    width = vars['width];
    vars = vars.add('width, (| w[1] |) || (width - i));
    args = ._eval_ctext(vars, args);
    return [.rewrap_lines(vars, (vars['header]) + (args[1]), " ".repeat(i)), (args[2]).add('width, width)];
    
    // $#Edited: 17 Jul 96 04:00 $kahuna
    // $#Edited: 17 Jul 96 00:11 $jenner
};

public method .do_dfn() {
    arg vars, flags, args;
    var ret;
    
    vars = vars.add('header, "");
    ret = .eval_indented(vars, args, 8);
    return [("\n" + (ret[1])) + "\n", ret[2]];
    
    // $#Edited: 18 Jul 96 19:25 $jenner
};


new object $j_html_format: $j_formatter;

var $root manager = $j_html_format;
var $root flags = ['variables, 'methods, 'code, 'core];
var $root created_on = 837902808;
var $root owners = [$j_html_format];
var $root inited = 1;
var $root managed = [$j_html_format];
var $root owned = [$j_html_format];

public method .do_hr() {
    arg vars, flags, args;
    
    return ["\n<hr>\n", vars];
    
    // $#Edited: 20 Jul 96 12:28 $kahuna
    // $#Edited: 20 Jul 96 16:46 $jenner
};

public method .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]];
    
    // $#Edited: 20 Jul 96 12:28 $kahuna
    // $#Edited: 20 Jul 96 16:46 $jenner
};

public method .do_strong() {
    arg vars, flags, args;
    var out, ret, a;
    
    ret = ._eval_ctext(vars, args);
    return [("<strong>" + (ret[1])) + "</strong>", ret[2]];
    
    // $#Edited: 20 Jul 96 12:28 $kahuna
    // $#Edited: 20 Jul 96 16:46 $jenner
};

public method .do_dl() {
    arg vars, flags, args;
    var out, ret, token;
    
    if ((| flags.getkey("compact") |))
        out = "\n<dl compact>\n";
    else
        out = "\n<dl>\n";
    ret = ._eval_ctext(vars, args);
    return [(out + (ret[1])) + "\n</dl>\n", ret[2]];
    
    // $#Edited: 20 Jul 96 12:28 $kahuna
    // $#Edited: 20 Jul 96 16:46 $jenner
};

public method .do_anchor() {
    arg vars, flags, args;
    var ret;
    
    ret = ._eval_ctext(vars, [args[1]]);
    return [[("/" + ((ret[1])[1])) + "/"], ret[2]];
    
    // $#Edited: 20 Jul 96 12:28 $kahuna
    // $#Edited: 20 Jul 96 16:46 $jenner
};

public method .do_p() {
    arg vars, args, flags;
    
    return ["\n<p>\n", vars];
    
    // $#Edited: 20 Jul 96 12:28 $kahuna
    // $#Edited: 20 Jul 96 16:46 $jenner
};

public method .do_em() {
    arg vars, flags, args;
    var out, ret, a;
    
    ret = ._eval_ctext(vars, args);
    return [("<em>" + (ret[1])) + "</em>", ret[2]];
    
    // $#Edited: 20 Jul 96 12:28 $kahuna
    // $#Edited: 20 Jul 96 16:46 $jenner
};

public method .do_br() {
    arg vars, args, flags;
    
    return ["\n<br>", vars];
    
    // $#Edited: 20 Jul 96 12:28 $kahuna
    // $#Edited: 20 Jul 96 16:46 $jenner
};

public method .do_bold() {
    arg vars, flags, args;
    var out, ret, a;
    
    ret = ._eval_ctext(vars, args);
    return [("<bold>" + (ret[1])) + "</bold>", ret[2]];
    
    // $#Edited: 20 Jul 96 12:28 $kahuna
    // $#Edited: 20 Jul 96 16:46 $jenner
};

public method .do_link() {
    arg vars, flags, args;
    var ret, node;
    
    ret = ._eval_ctext(vars, [args[1]]);
    node = (| flags.getkey("node") |);
    if (node)
        return [((("<a href=\"http://cold.org:1180/bin/help?" + node) + "\">") + (ret[1])) + "</a>", ret[2]];
    return ["&gt;&gt;ERROR: Invalid node&lt;&lt;", ret[2]];
    
    // $# Edited 26 Oct 1995 00:43 Lynx ($lynx)
    // $#Edited: 20 Jul 96 12:28 $kahuna
    // $#Edited: 20 Jul 96 16:46 $jenner
};

public method .do_web() {
    arg vars, flags, args;
    var ret;
    
    ret = ._eval_ctext(vars, [args[1]]);
    return [(((((ret[1]) + "<a href=\"") + (flags.getkey("src"))) + "\">") + (flags.getkey("name"))) + "</a>", ret[2]];
    
    // $#Edited: 20 Jul 96 12:28 $kahuna
    // $#Edited: 20 Jul 96 16:47 $jenner
};

public method .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];
    
    // $#Edited: 20 Jul 96 12:28 $kahuna
    // $#Edited: 20 Jul 96 16:47 $jenner
};

public method .do_action() {
    arg vars, flags, args;
    var ret;
    
    ret = ._eval_ctext(vars, args[1]);
    (| (vars['receiver]).register_action(ret[1], vars['this], flags.getkey("token")) |);
    return [("\\" + (ret[1])) + "\\", ret[2]];
    
    // $#Edited: 20 Jul 96 12:28 $kahuna
    // $#Edited: 20 Jul 96 16:47 $jenner
};

public method .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.getkey("level") |) || 3)) || 1);
    return [((((("<h" + l) + ">") + (ret[1])) + "</h") + l) + ">\n", ret[2]];
    
    // $#Edited: 20 Jul 96 12:28 $kahuna
    // $#Edited: 20 Jul 96 16:47 $jenner
};

public method .do_dd() {
    arg vars, flags, args;
    var ret;
    
    ret = ._eval_ctext(vars, args);
    return ["<dd>" + (ret[1]), vars];
    
    // $#Edited: 20 Jul 96 12:28 $kahuna
    // $#Edited: 20 Jul 96 16:47 $jenner
};

public method .do_b() {
    arg vars, flags, args;
    var out, ret, a;
    
    ret = ._eval_ctext(vars, args);
    return [("<b>" + (ret[1])) + "</b>", ret[2]];
    
    // $#Edited: 20 Jul 96 12:28 $kahuna
    // $#Edited: 20 Jul 96 16:47 $jenner
};

public method .do_i() {
    arg vars, flags, args;
    var out, ret, a;
    
    ret = ._eval_ctext(vars, args);
    return [("<i>" + (ret[1])) + "</i>", ret[2]];
    
    // $#Edited: 20 Jul 96 12:28 $kahuna
    // $#Edited: 20 Jul 96 16:47 $jenner
};

public method .do_dt() {
    arg vars, flags, args;
    var ret, term;
    
    ret = ._eval_ctext(vars, args);
    return ["\n<dt>" + (ret[1]), ret[2]];
    
    // $#Edited: 20 Jul 96 12:28 $kahuna
    // $#Edited: 20 Jul 96 16:47 $jenner
};

public method .do_li() {
    arg vars, flags, args;
    var ret;
    
    ret = ._eval_ctext(vars, args);
    return ["\n<li>" + (ret[1]), ret[2]];
    
    // $#Edited: 20 Jul 96 12:28 $kahuna
    // $#Edited: 20 Jul 96 16:47 $jenner
};

public method .do_lh() {
    arg vars, flags, args;
    var ret;
    
    ret = ._eval_ctext(vars, args);
    return ["\n<lh>" + (ret[1]), ret[2]];
    
    // $#Edited: 20 Jul 96 12:28 $kahuna
    // $#Edited: 20 Jul 96 16:47 $jenner
};

public method .do_tr() {
    arg vars, flags, args;
    var ret;
    
    ret = ._eval_ctext(vars, args);
    return ["\n<tr>" + (ret[1]), ret[2]];
    
    // $#Edited: 20 Jul 96 12:28 $kahuna
    // $#Edited: 20 Jul 96 16:47 $jenner
};

public method .do_td() {
    arg vars, flags, args;
    var ret, f, n;
    
    f = "";
    if ((n = (| flags.getkey("rowspan") |)))
        f += " rowspan=" + n;
    if ((n = (| flags.getkey("colspan") |)))
        f += " colspan=" + n;
    ret = ._eval_ctext(vars, args);
    return [(("\n<td" + f) + ">") + (ret[1]), ret[2]];
    
    // $#Edited: 20 Jul 96 12:28 $kahuna
    // $#Edited: 20 Jul 96 16:47 $jenner
};

public method .do_table() {
    arg vars, flags, args;
    var ret;
    
    ret = ._eval_ctext(vars, args);
    return [("\n<table>" + (ret[1])) + "\n</table>\n", ret[2]];
    
    // $#Edited: 20 Jul 96 12:28 $kahuna
    // $#Edited: 20 Jul 96 16:47 $jenner
};

public method .do_dfn() {
    arg vars, flags, args;
    var out, ret, a;
    
    ret = ._eval_ctext(vars, args);
    args = ret[1];
    vars = ret[2];
    return [("<exmp>" + args) + "</exmp>", vars];
};

public method .do_quote() {
    arg vars, flags, args;
    
    return [("<pre>" + ((((args[1]).replace("&", "&amp;")).replace("<", "&lt;")).replace(">", "&gt;"))) + "</pre>", vars];
    
    // $#Edited: 01 Aug 96 21:59 $jenner
};


new object $j_uncompiler: $j_evaluator;

var $root manager = $j_uncompiler;
var $root flags = ['variables, 'methods, 'code, 'core];
var $root created_on = 837583986;
var $root owners = [$j_uncompiler];
var $root inited = 1;
var $root managed = [$j_uncompiler];
var $root owned = [$j_uncompiler];

public method .quote() {
    arg s;
    
    return (((s.replace("[", "\[")).replace("]", "\]")).replace("{", "\{")).replace("=", "\=");
    
    // $#Edited: 16 Jul 96 11:27 $kahuna
    // $#Edited: 17 Jul 96 00:05 $kahuna
    // $#Edited: 17 Jul 96 00:13 $jenner
};

public method .eval_generator() {
    arg vars, gen;
    var out, flags, key, value, ret;
    
    catch ~methodnf {
        ret = .(gen.method())(vars, gen.ctext_flags(), gen.args());
        return [ret[1], ret[2]];
    } with {
        out = ["[" + (gen.name())];
        flags = gen.ctext_flags();
        for key in (flags) {
            if ((key[2]) == 1) {
                out = out.affix(" " + (key[1]));
            } else {
                ret = (> ._eval_ctext(vars, [flags[key]], 1) <);
                out = (out.affix((" " + (key[1])) + "=")).affix(ret[1]);
                vars = ret[2];
            }
        }
        if (gen.args()) {
            out = out.affix(":");
            for key in (gen.args()) {
                ret = (> ._eval_ctext(vars, [key]) <);
                out = out.affix(ret[1]);
                vars = ret[2];
            }
        }
        return [out.affix("]"), vars];
    }
    
    // $#Edited: 16 Jul 96 11:27 $kahuna
    // $#Edited: 17 Jul 96 00:05 $kahuna
    // $#Edited: 17 Jul 96 05:14 $kahuna
    // $#Edited: 17 Jul 96 00:13 $jenner
};

public method ._eval_ctext() {
    arg vars, data, [quote_all];
    var out, uflags, ret, token, a;
    
    out = [];
    if (type(data) != 'list)
        data = [data];
    for token in (data) {
        switch (type(token)) {
            case 'frob:
                if (class(token) == $j_generator) {
                    ret = (> .eval_generator(vars, token) <);
                    out = out.affix(ret[1]);
                    vars = ret[2];
                } else if (class(token) == $j_format) {
                    ret = (> .eval_formatter(vars, token) <);
                    out = out.affix(ret[1]);
                    vars = ret[2];
                }
            case 'string:
                out = out.affix(quote_all ? .quote_all(token) : (.quote(token)));
            default:
                out = out.affix(toliteral(token));
        }
    }
    return [out, vars];
    
    // $#Edited: 12 Jun 96 20:15 $levi
    // $#Edited: 16 Jul 96 11:27 $kahuna
    // $#Edited: 17 Jul 96 00:05 $kahuna
    // $#Edited: 17 Jul 96 00:13 $jenner
};

public method .eval_formatter() {
    arg vars, gen;
    var out, flags, key, value, ret;
    
    catch ~methodnf {
        ret = .(gen.method())(vars, gen.ctext_flags(), gen.args());
        return [ret[1], ret[2]];
    } with {
        out = ["{" + (gen.name())];
        flags = gen.ctext_flags();
        for key in (flags) {
            if ((key[2]) == 1) {
                out = out.affix(" " + (key[1]));
            } else {
                ret = ._eval_ctext(vars, key[2], 1);
                out = (out.affix((" " + (key[1])) + "=")).affix(ret[1]);
                vars = ret[2];
            }
        }
        if (gen.args()) {
            out = out.affix(":");
            for key in (gen.args()) {
                ret = ._eval_ctext(vars, [key]);
                out = out.affix(ret[1]);
                vars = ret[2];
            }
        }
        out = out.affix("}");
        return [out, vars];
    }
    
    // $#Edited: 16 Jul 96 11:27 $kahuna
    // $#Edited: 17 Jul 96 00:05 $kahuna
    // $#Edited: 17 Jul 96 05:14 $kahuna
    // $#Edited: 17 Jul 96 00:13 $jenner
};

public method .quote_all() {
    arg s;
    
    return (((((s.replace("[", "\[")).replace(" ", "\ ")).replace("]", "\]")).replace("{", "\{")).replace(":", "\:")).replace("=", "\=");
    
    // $#Edited: 16 Jul 96 11:27 $kahuna
    // $#Edited: 17 Jul 96 00:05 $kahuna
    // $#Edited: 17 Jul 96 00:13 $jenner
};

public method .do_quote() {
    arg vars, flags, args;
    
    return [(["{quote "].affix((args[1]).explode("\n", 1))).affix("}"), vars];
    
    // $#Edited: 02 Aug 96 18:01 $jenner
};


new object $admin: $programmer, $system_object;

var $root owners = [$admin];
var $root inited = 1;
var $root owned = [$admin];
var $root manager = $admin;
var $root quota = 75000;
var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'core, 'command_cache, 'variables];
var $root managed = [$admin];
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 formatter = #514;
var $user parsers = [$command_parser];
var $user tell_traceback = ['verbose, 4];
var $user context = #[];
var $user filters = [];
var $user task_connections = #[];
var $programmer eval_prefix = #[["me", "me = this()"], ["here", "here = this().location()"]];
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, 0]]];
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, []]]]]]], ["@ustat", [["@ustat", "*", "@ustat <any>", 'user_stat_cmd, #[[1, ['any, []]]]]]], ["@mojo", [["@mojo", "*", "@mojo <any>", 'mojo_cmd, #[[1, ['any, []]]]]]], ["@rehash-all", [["@rehash-all", "", "@rehash-all", 'rehash_all_cmd, #[]]]], ["@adjust|@promote", [["@adjust|@promote", "* to *", "@adjust|@promote <user> to <any>", 'adjust_user_cmd, #[[1, ['user, []]], [3, ['any, []]]]]]]];
var $help_ui indices = [$help_index_root];
var $help_ui history = [$help_summary];
var $help_ui current = 1;
var $user info = #[];
var $channel_ui channel_dict = #[];
var $channel_ui active_channels = #[];

root method .init_admin() {
    $sys.new_admin();
};

public method .del_task_cmd() {
    arg cmdstr, com, task;
    var syn, sys;
    
    // *xxxx is used to refer to the raw task, to be passed directly to
    // cancel() this is not a suggested method to take.
    (> .perms(sender(), 'this) <);
    (> .check_mojo() <);
    syn = com + " [*]<task ID>";
    if (task && ((task[1]) == "*")) {
        task = substr(task, 2);
        sys = 1;
    }
    task = toint(task);
    if (!task)
        (> .tell_error(syn, "Tasks must be given as an integer") <);
    if (sys) {
        cancel(task);
        .tell(("System task " + tostr(task)) + " canceled.");
    } else {
        catch any
            $scheduler.del_task(task);
        with
            (> .tell_error(syn, (traceback()[1])[2]) <);
        .tell(("Task " + tostr(task)) + " deleted.");
    }
};

public method .tasks_cmd() {
    arg cmdstr, com;
    var out, cdc, task, task_queue, line, len_c1, time, args, fmt, tfmt;
    
    (> .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 = [strfmt(fmt, "ID", "Exec Time", "Task"), strfmt(fmt, "---", "---------", "----")];
        fmt = fmt + ".%s(%s)";
        for task in (task_queue) {
            args = toliteral(task[8]);
            out += [strfmt(fmt, task[1], $time.format(tfmt, (task[2]) + (task[3])), task[4], task[6], substr(args, 2, strlen(args) - 2))];
        }
        if (out)
            out = ["--- Database Tasks"] + out;
        if ((cdc = tasks()))
            out += ["--- Preempted/Forked Task List"] + (cdc.prefix("  "));
    
        // once tasks() returns more intelligent info, we can parse it here
        return out + ["---"];
    } else {
        .tell("No Preempted/Forked tasks.");
    }
};

public method .backup_cmd() {
    arg cmdstr, com;
    
    (> .perms(sender(), 'this) <);
    (> .check_mojo() <);
    .tell("Starting backup.");
    $sys.do_backup(this());
    .tell("Done.");
};

protected method .shutdown_cmd() {
    arg cmdstr, com, [args];
    var time, opt, why, answer, ans;
    
    (> .check_mojo() <);
    args = $parse_lib.getopt(args.join(), [["t?ime", 1]]);
    opt = "t?ime" in ((args[2]).slice(1));
    if (opt && ((((args[2])[opt])[4]).is_numeric()))
        time = toint(((args[2])[opt])[4]);
    else
        time = 5;
    if (!(why = (args[1]).join()))
        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);
    
    // $#Edited: 18 Jul 96 14:56 $levi
};

protected method .adjust_user_cmd() {
    arg cmdstr, cmd, who, prep, str;
    var i, what, p, pwd, o;
    
    (> .check_mojo() <);
    what = lowercase((str.explode()).last());
    if (!(what in ["user", "builder", "programmer", "admin"]))
        return "Promote to: user, builder, programmer or admin.";
    
    // selectively munge their parents list, retain special parents
    p = who.parents();
    for o in ([$user, $builder, $programmer, $admin])
        p = setremove(p, o);
    
    // add the parent we care about to the front, call chparents()
    // we are admins, we can handle tracebacks if they occur.
    (> who.chparents(lookup(tosym(what)), @p) <);
    
    // notify them of what occurred
    what = ((what.a_or_an()) + " ") + what;
    who.tell((((.name()) + " has made you ") + what) + ".");
    
    // if they were a guest, set a password for them.
    if ($guest in p) {
        (> who.chparents(@setremove(who.parents(), $guest)) <);
        pwd = $code_lib.random_word();
        who.set_password(pwd);
        who.tell(("** Your password is currently \"" + pwd) + "\".");
        who.tell(("** You can change it with the command: `@password " + pwd) + " <new password>`");
        who.set_title("");
    }
    who.rehash_caches();
    .tell(((("Ok, " + (who.name())) + " is now ") + what) + ".");
    (.location()).announce(((("*poof* " + (who.name())) + " is now ") + what) + ".", who, this());
    
    // $#Edited: 10 Jul 96 01:03 $levi
};

protected method .grep_cmd() {
    arg [args];
    
    .check_mojo('benice);
    return (> pass(@args) <);
};

public method .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])) + ": [";
            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 method .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.";
};

protected method .mojo_cmd() {
    arg cmdstr, cmd, str;
    var syn, line;
    
    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 glow as Mojo courses through ") + ((.gender()).pronoun('pp))) + " body.", this());
            $sys.add_to_system(this());
        case "off", "down":
            (.location()).announce((((.name()) + "'s eyes stop glowing as the Mojo leaves ") + ((.gender()).pronoun('pp))) + " body.", this());
            $sys.del_from_system(this());
    }
    .tell(.mojo(1));
    
    // $#Edited: 10 Jun 96 19:35 $levi
};

public method .description() {
    arg flags;
    
    return (> pass(flags) <) + [.mojo()];
};

public method .logout() {
    arg [args];
    
    (| $sys.del_from_system(this()) |);
    return (> pass(@args) <);
};

protected method .check_mojo() {
    arg [benice];
    var answer, wording;
    
    if (benice)
        wording = " may require ";
    else
        wording = " requires ";
    if (!(this() in ($sys.system()))) {
        answer = .prompt(("This command" + wording) + "Mojo, turn it on? [yes] ");
        if (answer in ["no", "n"]) {
            .tell("Ok, aborting.");
            if (benice)
                return;
            throw(~stop, "", 'no_traceback);
        }
        .mojo_cmd("", "", "on");
    }
};

public method .rehash_all_cmd() {
    arg cmdstr, cmd;
    var c, p, o, d, descendants;
    
    (> .check_mojo() <);
    .tell("Building descendants list..");
    pause();
    pause();
    descendants = $has_commands.descendants();
    .tell("Purging Caches..");
    pause();
    pause();
    
    // go atomic, we do not want confused users trying to run commands that
    // do not exist...
    atomic(1);
    
    // purge EVERYTHING, MUAHAHAHA
    for o in (descendants) {
        if ((| o.is_command_cache() |)) {
            .tell("Purging " + (o.namef('ref)));
            (| o.purge_caches() |);
        }
        refresh();
    }
    
    // Mission critical now, we must get these caches back..
    for o in ((| $user_db.connected() |) || [this()]) {
        for p in ((| o.parents() |) || []) {
            .tell("Initializing " + (p.namef('ref)));
            (| p.cache_init() |);
        }
        refresh();
    }
    
    // ok, good...
    atomic(0);
    return "Done...";
};


new object $help_node: $help_root, $named;

var $root child_index = 6;
var $root fertile = 1;
var $root inited = 1;
var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'fertile, 'variables, '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 = [];
var $root trusted_by = [$help_index_root, $help_index_core];
var $root manager = $help_node;
var $editable types = ["help"];
var $root managed = [$help_node];
var $root owners = [$help_node];
var $root owned = [$help_node];

root method .init_help_node() {
    linked_by = [];
    links = #[];
    
    // title = "";
    body = $j_ctext_frob.new([]);
    
    // upnodes = [];
    // downnodes = [];
    // $#Edited: 18 Jul 96 19:06 $jenner
};

root method .uninit_help_node() {
    var obj;
    
    if (linked) {
        for obj in (linked)
            (| obj.node_going_away() |);
    }
    if (indices) {
        for obj in (indices)
            (| obj.node_going_away() |);
    }
    indices = [];
    linked = [];
    links = #[];
    title = "";
    body = [];
    
    // $#Edited: 18 Jul 96 18:31 $jenner
};

public method .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 method .set_body() {
    arg new_body;
    var new_body, anchors, key, keys, values, value;
    
    (> .perms(sender()) <);
    
    // Compile a string into help ctext
    new_body = $j_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) |) || #[];
    body = (<$j_ctext_frob, [body._ctext(), (| (body.vars()).del('links) |) || (body.vars())]>);
    keys = anchors.keys();
    values = anchors.values();
    links = #[];
    for key in (keys)
        links = links.add(key, $object_lib.to_dbref(anchors[key]));
    
    // $#Edited: 03 Mar 96 15:33 Lynx ($lynx)
    // $#Edited: 18 Jul 96 18:55 $jenner
};

public method .body() {
    return body;
};

public method .links() {
    return links;
};

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

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

public method .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 method .eval_body() {
    return body.eval_ctext();
};

public method .add_index() {
    arg index;
    
    if (!(caller() == $help_editing_ui))
        (> .perms(caller(), 'this) <);
    indices = (indices || []).setadd(index);
    index.add_help_node(this());
    
    // $#Edited: 22 Jul 96 11:54 $jenner
};

public method .del_index() {
    arg index;
    
    (> .perms(caller(), 'this) <);
    indices = indices.setremove(index);
    index.del_help_node(this());
    if (!indices)
        .add_index($help_index_root);
    
    // $#Edited: 18 Jul 96 18:25 $jenner
};

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

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

public method .title() {
    return title;
};

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

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

public method ._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 method .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 method .downnode_names() {
    var output, t;
    
    output = [];
    for t in (.downnodes())
        output = [@output, t.title()];
    return output;
};

public method .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 method .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().mmap('name).to_english());
    //if (node.downnodes())
    //    .tell("Down-nodes: " + node.downnodes().mmap('name).to_english());
};

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

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

public method .set_name() {
    arg new_name, [ignore];
    var i;
    
    if (indices) {
        for i in (indices)
            (| i.key_changed(.name(), new_name) |);
    }
    (> pass(new_name, 'prop) <);
    
    // $#Edited: 18 Jul 96 19:18 $jenner
};

public method .edit_help() {
    var p;
    
    (> .perms(sender()) <);
    p = (.body()).uncompile();
    (> sender().invoke_editor(this(), '_edit_help_callback, p, []) <);
    
    // $#Edited: 18 Aug 96 21:02 $jenner
};

public method ._edit_help_callback() {
    arg text, client_data;
    
    (> .perms(sender()) <);
    .set_body(text);
    return "Helpnode body set.";
    
    // $#Edited: 18 Aug 96 20:14 $jenner
    // $#Moved 18 Aug 96 21:29 from $help._edit_help_callback() by $jenner
};


new object $help_summary: $help_node;

var $root manager = $help_summary;
var $root inited = 1;
var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'variables, 'core];
var $named name = ['prop, "Summary", "Summary"];
var $named name_aliases = [];
var $help_node linked_by = [];
var $help_node links = #[["Help", #701], ["Overview", #712], ["Commands", #714], ["Interaction", #959], ["Interface", $help_summary], ["Building", #711], ["Theme", #699], ["Programming", #729]];
var $help_node body = <$j_ctext_frob, [[<$j_format, ["hr", [], [], 'do_hr]>, <$j_format, ["quote", [], ["\n\n      ___     _    _   _  _     _        ___         _             \n     / __|___| |__| | | || |___| |_ __  / __|_  _ __| |_ ___ _ __  \n    | (__/ _ \ / _` | | __ / -_) | '_ \ \__ \ || (_-<  _/ -_) '  \ \n     \___\___/_\__,_| |_||_\___|_| .__/ |___/\_, /__/\__\___|_|_|_|\n                                 |_|         |__/                  \n\n"], 'do_quote]>, " Welcome to ", <$j_generator, ["server_name", [], [], 'gen_servername]>, ". This is a hypertext help system. If you are on an interactive login (terminal, not the web) you can type ", <$j_format, ["tt", [], ["@help help"], 'do_tt]>, " for help on how to use this help system. Following is a list of Launch Points to the help system:", <$j_format, ["p", [], [], 'do_p]>, " ", <$j_format, ["dl", [["compact", 1]], [" ", <$j_format, ["dt", [], [<$j_format, ["link", [["node", "$help_node_help"]], ["Help"], 'do_link]>], 'do_dt]>, " ", <$j_format, ["dd", [], ["How to use this help system"], 'do_dd]>, " ", <$j_format, ["dt", [], [<$j_format, ["link", [["node", "$help_overview"]], ["Overview"], 'do_link]>], 'do_dt]>, " ", <$j_format, ["dd", [], ["Overview of how ", <$j_generator, ["server_name", [], [], 'gen_servername]>, "functions"], 'do_dd]>, " ", <$j_format, ["dt", [], [<$j_format, ["link", [["node", "$help_commands"]], ["Commands"], 'do_link]>], 'do_dt]>, " ", <$j_format, ["dd", [], ["How and why commands work"], 'do_dd]>, " ", <$j_format, ["dt", [], [<$j_format, ["link", [["node", "$help_interaction"]], ["Interaction"], 'do_link]>], 'do_dt]>, " ", <$j_format, ["dd", [], ["How to interact with others"], 'do_dd]>, " ", <$j_format, ["dt", [], [<$j_format, ["link", [["node", "$help_summary"]], ["Interface"], 'do_link]>], 'do_dt]>, " ", <$j_format, ["dd", [], ["How to tweak your interface"], 'do_dd]>, " ", <$j_format, ["dt", [], [<$j_format, ["link", [["node", "$help_building"]], ["Building"], 'do_link]>], 'do_dt]>, " ", <$j_format, ["dd", [], ["How to extend the Virtual Environment"], 'do_dd]>, " ", <$j_format, ["dt", [], [<$j_format, ["link", [["node", "$help_theme"]], ["Theme"], 'do_link]>], 'do_dt]>, " ", <$j_format, ["dd", [], ["The theme to ", <$j_generator, ["server_name", [], [], 'gen_servername]>], 'do_dd]>, " ", <$j_format, ["dt", [], [<$j_format, ["link", [["node", "$help_driver"]], ["Programming"], 'do_link]>], 'do_dt]>, " ", <$j_format, ["dd", [], ["Topics about programming"], 'do_dd]>, " "], 'do_dl]>], #[['this, $help_summary]]]>;
var $help_node indices = [$help_index_root];
var $root managed = [$help_summary];
var $root owners = [$help_summary];
var $root owned = [$help_summary];


new object $db: $misc;

var $root inited = 1;
var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'core, 'variables];
var $db database = #[];
var $root manager = $db;
var $root managed = [$db];
var $root owners = [$db];
var $root owned = [$db];

root method .init_db() {
    database = #[];
    
    // $#Edited: 09 May 96 20:48 $jenner
};

public method .database() {
    return database;
    
    // $#Edited: 09 May 96 20:48 $jenner
};

public method .value_changed() {
    arg key, new_value;
    
    // change the value of a key.
    if ((!(sender() == this())) && (!(.trusts(caller()))))
        (> .perms(sender(), 'writer) <);
    (> .remove(key) <);
    (> .insert(key, new_value) <);
    
    // $#Edited: 09 May 96 20:48 $jenner
};

public method .remove() {
    arg key;
    
    // remove a key/value from the database
    if ((!(sender() == this())) && (!(.trusts(caller()))))
        (> .perms(sender(), 'writer) <);
    database = database.del(key);
    
    // $#Edited: 09 May 96 20:48 $jenner
};

public method .exact_match() {
    arg key;
    var match;
    
    // get an exact match of a key, return the value
    match = (| (type(database) == 'dictionary) ? database[key] : ((database.match_exact(key))[2]) |);
    if (match == ~keynf)
        throw(~matchnf, "No object by that key exists in the database.");
    return match;
    
    // $#Edited: 09 May 96 20:48 $jenner
};

public method .match_begin() {
    arg key;
    var matches, entry;
    
    // First check if we're using $trie frob
    if (type(database) == 'frob) {
        matches = (| (database.match_begin(key))[2] |);
        if (matches == ~keynf)
            throw(~matchnf, "No entries in the database match that key.");
        if (matches == ~ambig)
            throw(~ambig, "More than one object matches that key.");
        return matches;
    }
    
    // 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.");
    }
    
    // $#Edited: 09 May 96 20:48 $jenner
};

public method .insert() {
    arg key, value;
    
    // insert a key/value to the database
    if ((!(sender() == this())) && (!(.trusts(caller()))))
        (> .perms(sender(), 'writer) <);
    database = database.add(key, value);
    
    // $#Edited: 09 May 96 20:48 $jenner
};

root method .uninit_db() {
    database = 0;
    
    // $#Edited: 09 May 96 20:48 $jenner
};

public method .key_changed() {
    arg old_key, new_key;
    var val;
    
    // change the value of a key.
    if ((!(sender() == this())) && (!(.trusts(caller()))))
        (> .perms(sender(), 'writer) <);
    val = (> .exact_match(old_key) <);
    .remove(old_key);
    .insert(new_key, val);
    
    // $#Edited: 09 May 96 20:48 $jenner
    // $#Edited: 20 Jul 96 16:37 $jenner
};


new object $registry: $db;

var $root fertile = 1;
var $root inited = 1;
var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'fertile, 'variables, '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 = [];
var $root manager = $registry;
var $root managed = [$registry];
var $root owners = [$registry];
var $root owned = [$registry];

root method .uninit_registry() {
    reserved_names = 0;
    invalid_names = 0;
    trusted = 0;
    stripped_characters = 0;
};

public method .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 method .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 method .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 method .set_stripped_characters() {
    arg string;
    
    if (!(sender() in [.manager(), this()]))
        throw(~perm, "Permission denied");
    if (type(string) != 'string)
        throw(~type, "Value is not a string.");
    stripped_characters = string;
    
    // $#Edited: 18 Jul 96 17:53 $jenner
};

public method .insert() {
    arg name, obj;
    
    // registers obj with obj.name
    if ((!(.trusts(caller()))) && ((!sender()) == this()))
        throw(~perm, "Permission denied.");
    if (stripped_characters)
        name = $string.strip(name, stripped_characters);
    (> pass(name, obj) <);
    
    // $#Edited: 09 May 96 20:57 $jenner
    // $#Edited: 18 Jul 96 18:02 $jenner
};

public method .remove() {
    arg name;
    
    // removes the object from the database.
    // THIS: is what is broken with guests, should fix it.
    if ((!(.trusts(caller()))) && (sender() != this()))
        throw(~perm, "Permission denied.");
    if (stripped_characters)
        name = $string.strip(name, stripped_characters);
    (> pass(name) <);
    
    // $#Edited: 09 May 96 20:57 $jenner
    // $#Edited: 18 Jul 96 18:06 $jenner
};

public method .database() {
    if (!(.has_flag('variables, sender())))
        throw(~perm, "Database is not readable by sender.");
    return (> pass() <);
};

public method .exact_match() {
    arg name;
    
    // returns a direct match of the name (if there is one)
    if (!(.has_flag('variables, sender())))
        throw(~perm, "Database is not readable by sender.");
    if (stripped_characters)
        name = $string.strip(name, stripped_characters);
    return (> pass(name) <);
};

public method .valid_name() {
    arg name;
    var word, sname, matched_obj;
    
    // returns 1 if the name is valid
    /// if (!.has_flag('variables,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 ((| (matched_obj = .exact_match(name)) |)) {
        if (matched_obj != sender())
            throw(~invname, ("The name \"" + name) + "\" is already taken.");
    }
    
    // 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 (name.match_pattern(("*" + word) + "*"))
                    throw(~invname, ("`" + word) + "' is not allowed as part of a name.");
            }
        }
    }
};

public method .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 method .stripped_characters() {
    return stripped_characters;
};

public method .key_changed() {
    arg old_name, new_name;
    
    // adjusts the database for the new name
    if ((!(.trusts(caller()))) && (sender() != this()))
        throw(~perm, "Permission denied.");
    if (stripped_characters) {
        old_name = $string.strip(old_name, stripped_characters);
        new_name = $string.strip(new_name, stripped_characters);
    }
    (> pass(old_name, new_name) <);
    
    // $#Edited: 18 Jul 96 18:33 $jenner
};

public method .search() {
    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 {
        switch (error()) {
            case ~ambig:
                rethrow(error());
            default:
                throw(~namenf, "No matches found.");
        }
    }
    return tmp;
};


new object $mail_db: $registry, $mail_root;

var $root trusted = [$mail_list];
var $root inited = 1;
var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'core, 'variables];
var $db database = #[["chatter", #594], ["core", #597], ["server", #595], ["qooc", #596], ["news", $mail_list_news], ["bugs", #718]];
var $root manager = $mail_db;
var $root managed = [$mail_db];
var $root owners = [$mail_db];
var $root owned = [$mail_db];

public method .valid_recipient() {
    arg recip;
    
    if (recip.has_ancestor($mail_list))
        return 1;
    return 0;
};

public method .mail_name() {
    arg obj;
    
    return "*" + (obj.name());
    
    // $# Edited 05 Nov 1995 14:04 Lynx ($lynx)
};


new object $place_db: $registry;

var $root trusted = 1;
var $root inited = 1;
var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'variables, 'core];
var $db database = #[["void", $void], ["nowhere", $nowhere], ["The Pit", $the_pit]];
var $registry stripped_characters = "!@#$%^&*()_+-=~`'{}[]|/?\",.<>;:";
var $root manager = $place_db;
var $root managed = [$place_db];
var $root owners = [$place_db];
var $root owned = [$place_db];

public method .place_destroyed() {
    // called in $place.uninit_place (incase the place is in the db)
};


new object $user_db: $registry;

var $root trusted = [$user];
var $root inited = 1;
var $root created_on = 796268969;
var $root flags = ['methods, 'variables, 'code, 'core];
var $db database = <$trie, [0, "GPRDNH", [0, "ue", [0, "e", [0, "s", [0, "t", [0, "bnsfhmloepcd", [0, "ierah", [["tdancer", #418], ""], [["owulf", #386], ""], [["ian", #458], ""], [["dger", #1010], ""], [["oedda", $j_telnet_format], ""]], [["etworker", #425], ""], [0, "ta", [["ragen", $network], ""], [["ndroth", #890], ""]], [["asteddie", $help_index], ""], [0, "a", [0, "rd", [["monic", $place_db], ""], [["es", #892], ""]]], [["iro", $data_lib], ""], [0, "oyu", [0, "ut", [["", #521], ""], [["h", #603], ""]], [["nxguest", $control], ""], [["c", #710], ""]], [["celot", #389], ""], [["eep", #1021], ""], [["eacock", $mail_lib], ""], [["arlhenry", #543], ""], [["izzy", #7], ""]]]]], [0, "n", [0, "e", [0, "r", [0, "i", [0, "c", [0, "BUGPA", [["uilder", $builder], ""], [["serObject", $user], ""], [["uestObject", $guest], ""], [["rogrammer", $programmer], ""], [["dmin", $admin], ""]]]]]]]], [["layer", $player], ""], [["eaper", $reaper], ""], [["izzy", #7], ""], [0, "o", [0, "Oe", [["ne", $no_one], ""], [["l", #1273], ""]]], [["obar", #1806], ""]]>;
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;
var $root manager = $user_db;
var $root managed = [$user_db];
var $root owners = [$user_db];
var $root owned = [$user_db];

public method .users() {
    return (.database()).keys();
};

public method .connected() {
    var x;
    
    for x in (connected) {
        if ((!valid(x)) || (| !(x.connections()) |))
            connected = connected.setremove(x);
    }
    return connected;
};

public method .did_connect() {
    (> .perms(caller(), $user) <);
    connected = connected.setadd(sender());
};

public method .did_disconnect() {
    .perms(caller(), $user);
    connected = connected.setremove(sender());
};

public method .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 method .match() {
    arg name;
    
    return (> .search(name) <);
};

public method .total_connected() {
    return (.connected()).length();
};

public method .clean_user_db() {
    var key, db, invalid;
    
    db = .database();
    invalid = [];
    for key in ((.database()).keys()) {
        if (!valid(db[key])) {
            .remove(key);
            connected = connected.setremove(key);
            invalid = [@invalid, key];
        }
    }
    return ["Invalid $user_db entries: " + (invalid.to_english())];
    
    // $#Edited: 09 May 96 20:51 $jenner
};


new object $help_index: $help_root, $named, $registry;

var $root child_index = 1;
var $root inited = 1;
var $root trusted = 1;
var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'variables, 'core];
var $named name = ['uniq, "help_index", "the help_index"];
var $named name_aliases = [];
var $db database = #[];
var $root manager = $help_index;
var $root managed = [$help_index];
var $root owners = [$help_index];
var $root owned = [$help_index];

root method .init_help_index() {
    .set_stripped_characters("!@#$%^&*()");
    .add_trusted($help_node);
    
    // $#Edited: 18 Jul 96 18:09 $jenner
    // $#Edited: 20 Jul 96 17:13 $jenner
};

root method .uninit_help_index() {
    var key;
    
    for key in ((.database()).keys())
        (.match_exact(key)).del_index(this());
    
    // $#Edited: 18 Jul 96 18:09 $jenner
};

public method .add_help_node() {
    arg node;
    
    if ((caller() != $help_node) && (!(.is_writable_by(caller()))))
        throw(~perm, "Permission denied.");
    .insert(node.name(), node);
    
    // $#Edited: 18 Jul 96 18:09 $jenner
};

public method .del_help_node() {
    arg node;
    
    if ((caller() != $help_node) && ((!(.is_writable_by(sender()))) && (sender() != this())))
        throw(~perm, "Permission denied.");
    .remove(node.name(), node);
    
    // $#Edited: 18 Jul 96 18:09 $jenner
};

public method .node_going_away() {
    .del_help_node(sender());
    
    // $#Edited: 18 Jul 96 18:09 $jenner
};


new object $help_index_root: $help_index;

var $root manager = $help_index_root;
var $root created_on = 806626103;
var $root inited = 1;
var $root flags = ['methods, 'code, 'variables, 'core];
var $named name = ['prop, "Index", "Index"];
var $named name_aliases = [];
var $db database = #[["List", #491], ["settings", #687], ["overview", #685], ["Cold Markup Language (CML)", #615], ["Interface", #706], ["Summary", $help_summary], ["Cml generators", #702], ["Navigation", #684], ["the help_node_1", #691], ["Programming", #747], ["Commands", #957], ["Building", #711], ["Interaction", #959], ["the help_node_5", #699], ["an Overview", #712], ["Help", #701], ["@show", #1021], ["@display", #1013], ["@help-link", #1010], ["a VR", #995], ["a Command Types", #715], ["a Matching Conventions", #1005], ["an Enhanced Command Templates", #1015], ["an Objects", #713], ["an Environment", #970], ["Cml formatters", #693], ["Cml programming", #728], ["@hnode", #784], ["@hwrite", #785], ["@hlist", #786], ["@program", #716], ["Driver", #729], ["Functions", #748], ["abs", #797], ["strfmt", #907], ["strgraft", #908], ["errors", #788], ["file intro", #789], ["intro", #790], ["method intro", #791], ["networking", #792], ["oop", #793], ["regular expressions", #794], ["security", #795], ["task intro", #796], ["add_method", #798], ["add_var", #799], ["ancestors", #800], ["atomic", #801], ["backup", #802], ["bind_function", #803], ["bind_port", #804], ["buf_replace", #805], ["buf_to_str", #806], ["buf_to_strings", #719], ["bufgraft", #807], ["buflen", #808], ["caller", #809], ["cancel", #810], ["children", #811], ["chparents", #812], ["class", #813], ["clear_var", #814], ["close_connection", #815], ["coldmud", #816], ["coldmud_5", #817], ["compile", #818], ["connection", #819], ["create", #820], ["crypt", #821], ["ctime", #822], ["cwrite", #823], ["cwritef", #824], ["data", #825], ["dblog", #826], ["decompile", #827], ["definer", #828], ["del_method", #829], ["del_objname", #830], ["del_var", #831], ["delete", #832], ["descendants", #833], ["destroy", #834], ["dict_add", #835], ["dict_contains", #836], ["dict_del", #837], ["dict_keys", #838], ["error", #839], ["execute", #749], ["explode", #840], ["fchmod", #841], ["fclose", #842], ["feof", #843], ["fflush", #844], ["file", #845], ["files", #846], ["find_method", #847], ["find_next_method", #848], ["fmkdir", #849], ["fopen", #850], ["fread", #851], ["fremove", #852], ["frename", #853], ["frmdir", #854], ["fseek", #855], ["fstat", #856], ["fwrite", #857], ["method_bytecode", #858], ["get_var", #859], ["has_ancestor", #860], ["insert", #861], ["join", #862], ["listgraft", #863], ["listlen", #864], ["localtime", #865], ["lookup", #866], ["lowercase", #867], ["match_begin", #868], ["match_pattern", #869], ["match_regexp", #870], ["match_template", #871], ["math", #872], ["max", #873], ["method", #874], ["method_access", #875], ["method_flags", #876], ["method_info", #877], ["methods", #878], ["min", #879], ["mtime", #880], ["objname", #881], ["objnum", #882], ["open_connection", #883], ["pad", #884], ["parents", #885], ["pause", #886], ["random", #887], ["reassign_connection", #888], ["refresh", #889], ["regexp", #890], ["rename_method", #891], ["replace", #892], ["resume", #893], ["rethrow", #894], ["sender", #895], ["set_heartbeat", #896], ["set_method_access", #897], ["set_method_flags", #898], ["set_objname", #899], ["set_var", #900], ["setadd", #901], ["setremove", #902], ["shutdown.html", #1497], ["size", #903], ["stack", #904], ["str_to_buf", #905], ["strcmp", #906], ["strings_to_buf", #909], ["strlen", #910], ["strsed", #911], ["strsub", #912], ["subbuf", #913], ["sublist", #914], ["substr", #915], ["suspend", #916], ["task_id", #917], ["tasks", #918], ["this", #919], ["throw", #920], ["tick", #921], ["ticks_left", #922], ["time", #923], ["toerr", #924], ["tofloat", #657], ["toint", #925], ["toliteral", #926], ["toobjnum", #927], ["tostr", #928], ["tosym", #929], ["traceback", #930], ["type", #931], ["unbind_function", #932], ["unbind_port", #933], ["union", #934], ["uppercase", #935], ["valid", #736], ["variables", #936], ["objects", #937], ["object methods", #938], ["referencing", #939], ["special", #940], ["object variables", #941], ["structure", #942], ["expressions", #751], ["statements", #730], ["tokens", #943], ["types", #944], ["Conventions", #787], ["shutdown", #945], ["expression variables", #946], ["operators", #947], ["calling functions", #948], ["calling methods", #949], ["error handling", #950], ["splicing", #951], ["core documents", #952], ["Trie", #665], ["simple statements", #953], ["conditional statements", #750], ["looping statements", #954], ["Error-Handling Statements", #955], ["type comparison", #956], ["looping expressions", #752], ["Editor", #731], ["invoking", #732], ["Channels", #958]];
var $root trusted = [$help_node];
var $root child_index = 2;
var $root managed = [$help_index_root];
var $root owners = [$help_index_root];
var $root owned = [$help_index_root];


new object $help_index_core: $help_index_root;

var $root manager = $help_index_core;
var $root flags = ['variables, 'methods, 'code, 'core];
var $root created_on = 837910430;
var $root owners = [$help_index_core];
var $db database = #[];
var $has_settings defined_settings = #[];
var $has_settings local_settings = #[];
var $has_settings settings = #[];
var $named name = ['proper, "help_index_root_2", "help_index_root_2"];
var $named name_aliases = [];
var $registry stripped_characters = "!@#$%^&*()";
var $root trusted = [$help_node];
var $root inited = 1;
var $root managed = [$help_index_core];
var $root owned = [$help_index_core];


new object $mail_list_news: $mail_list;

var $root fertile = 1;
var $root manager = $mail_list_news;
var $root inited = 1;
var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'fertile, 'core, 'variables];
var $named name = ['prop, "news", "news"];
var $named name_aliases = [];
var $mail_list mail = [];
var $mail_list senders = [#25, #2, #381];
var $mail_list readers = 1;
var $mail_list notify = [$mail_list_news, #2, #333, #1010, #616, #826, #797, #384, #601, #1007, #890, #743, $j_telnet_format, #1013, #592, #329, #345, #1348, #820, #625, #823, #1008, #73, #602, #1002, $robot, $event_frob, #412, #970, #890, #382, #376, #627, #616, #289, #603, #329, #329, #1794, #351, #406, #1941, #1994, #1995, #1999, #2001, #2002, #623, #616, #1731, #1773, #55, #1775, #1776, #665, #1850, #1919, #4, #2055, #2056, #2124, #2125];
var $mail_list last_received_on = 839350687;
var $root managed = [$mail_list_news];
var $root owners = [$mail_list_news];
var $root owned = [$mail_list_news];


new object $text: $foundation;

var $root fertile = 1;
var $root inited = 1;
var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'fertile, 'core, 'variables];
var $text text = 0;
var $root manager = $text;
var $root child_index = 1;
var $root managed = [$text];
var $root owners = [$text];
var $root owned = [$text];

root method .init_text() {
    text = [];
};

root method .uninit_text() {
    perm = [];
};

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

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

public method .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 method .del_text() {
    // deletes all text
    if ((!(.is_writable_by(sender()))) && (sender() != this()))
        throw(~perm, "Permission Denied.");
    text = [];
};

public method .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 method .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 method .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;
    }
};

public method .edit_text() {
    var p;
    
    (> .perms(sender()) <);
    p = .text();
    (> sender().invoke_editor(this(), '_edit_text_callback, p, []) <);
    
    // $#Edited: 18 Aug 96 21:02 $jenner
};

public method ._edit_text_callback() {
    arg t, client_data;
    
    (> .perms(sender()) <);
    .set_text(t);
    return "Text set.";
    
    // $#Edited: 18 Aug 96 20:26 $jenner
};


new object $note: $thing, $text;

var $root child_index = 40;
var $root fertile = 1;
var $root inited = 1;
var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'fertile, 'variables, 'core];
var $text text = [];
var $located location = $nowhere;
var $located obvious = 1;
var $described prose = [];
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, []]]]]]]];
var $root manager = $note;
var $editable types = ["text"];
var $root managed = [$note];
var $root owners = [$note];
var $root owned = [$note];

public method .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())) + ".");
        } else {
            .set_text((.text()) + ntext);
        }
        who.tell(((("Line" + (((ntext.length()) == 1) ? "" : "s")) + " added to ") + (.name())) + ".");
    } else {
        who.tell("Text not added.");
    }
};

public method .seperator() {
    return (type(seperator) == 'string) ? seperator : "---";
};

root method .init_note() {
    .del_flag('variables);
    .set_public('readable);
};

public method .init_for_core() {
    .perms(caller(), $sys);
};

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

public method .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 method .write_cmd() {
    arg cmdstr, cmd, [args];
    var who, line;
    
    who = sender();
    if ((!(.is_publicly('writable))) && (!(.is_writable_by(who))))
        return who.tell((.name()) + " is not publicly writable!");
    
    // because I'm odd lets do this all 1 one command.
    if ((args.length()) == 2) {
        line = "Now writing on " + (.name());
        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);
    }
    
    // $# Edited 05 Nov 1995 14:00 Lynx ($lynx)
};

public method .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((.name()) + " 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())) + ".");
        } 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()).length()));
            line = line + ($string.chop(oldline, len));
            line = (line + "\") erased from ") + (.name());
            who.tell(line);
        }
    } with {
        switch (error()) {
            case ~range:
                who.tell("There are not that many lines in the text.");
            default:
                who.tell("Oops: " + ((traceback()[1])[2]));
        }
    }
    
    // $# Edited 05 Nov 1995 14:00 Lynx ($lynx)
};

public method .erase_cmd() {
    arg cmdstr, cmd, this;
    var line, nline, len;
    
    if ((!(.is_publicly('writable))) && (!(.is_writable_by(sender()))))
        return sender().tell((.name()) + " 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())) + ".");
    
    // $# Edited 05 Nov 1995 14:01 Lynx ($lynx)
};

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

public method .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((.name()) + " 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);
    
    // $# Edited 05 Nov 1995 14:11 Lynx ($lynx)
};

public method .copy_cmd() {
    arg cmdstr, cmd, from, this, prep, dest;
    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))) + ".");
    dest = $parse_lib.ref(dest);
    obj = dest[3];
    if (!(obj.is_writable_by(sender())))
        return .tell(("!  " + (obj.namef('ref))) + " is not owned by you.");
    if ((obj.has_ancestor($note)) && (!(dest[4])))
        obj.add_text(text, sender());
    if ((dest[1]) == 'method) {
        catch any {
            dest = (> tosym(dest[4]) <);
            if (what) {
                obj.(dest)(text, sender());
                sender().tell(((("Text copied to " + obj) + ".") + dest) + "().");
            } else {
                if (!((| tosym(dest) |) in (obj.variables())))
                    return .tell(("!  variable '" + dest) + " not found");
                obj.eval([(dest + " = ") + (.text())]);
            }
        } with {
            sender().tell_error((traceback()[1])[2]);
        }
    }
    sender().tell("Text copied from " + (.name()));
    
    // $#Edited: 13 Jun 96 00:50 $levi
};

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


new object $log: $note;

var $root child_index = 4;
var $root fertile = 1;
var $root trusted = [];
var $root inited = 1;
var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'fertile, 'core];
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, []]]]]]]];
var $root manager = $log;
var $root managed = [$log];
var $root owners = [$log];
var $root owned = [$log];

public method .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(["---", (((((.name()) + ", entries ") + tostr(loglen - 10)) + " to ") + tostr(loglen)) + " (last 10 lines).", "---"]);
        sender().tell(text.subrange(loglen - 10));
        sender().tell("---");
    }
    
    // $# Edited 05 Nov 1995 14:03 Lynx ($lynx)
};

public method .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);
    }
};


new object $http_log: $log;

var $root inited = 1;
var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'core];
var $root owners = [$http_log];
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;
var $root manager = $http_log;
var $root managed = [$http_log];
var $root owned = [$http_log];

public method .log() {
    arg [args];
    
    tally = tally + 1;
};

public method .tally() {
    return tally;
};


new object $reaper_log: $log;

var $root inited = 1;
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 $described prose = [];
var $located location = $nowhere;
var $located obvious = 1;
var $root manager = $reaper_log;
var $root managed = [$reaper_log];
var $root owners = [$reaper_log];
var $root owned = [$reaper_log];


new object $login_log: $log;

var $root manager = $login_log;
var $root inited = 1;
var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'core];
var $public public = ['readable];
var $text text = ["15 Mar 96 21:32> [~invname, \"Names must have at least 3 alpha-numeric characters in them\", 0]", "15 Mar 96 21:32> ['method, 'valid_name, $user_db, $registry, 16]", "15 Mar 96 21:32> [~invname, 'valid_name, $user_db, $user_db, 5]", "15 Mar 96 21:32> [~invname, 'set_name, #1003, $user, 7]", "15 Mar 96 21:32> [~invname, 'create_user, $sys, $sys, 9]", "15 Mar 96 21:32> [~invname, 'connect_guest_cmd, $login_interface_3113, $login_interface, 27]", "17 Mar 96 14:57> [~invname, \"Name already exists.\", 0]", "17 Mar 96 14:57> ['method, 'valid_name, $user_db, $registry, 23]", "17 Mar 96 14:57> [~invname, 'valid_name, $user_db, $user_db, 5]", "17 Mar 96 14:57> [~invname, 'set_name, #1063, $user, 7]", "17 Mar 96 14:57> [~invname, 'create_user, $sys, $sys, 9]", "17 Mar 96 14:57> [~invname, 'connect_guest_cmd, $login_interface_3132, $login_interface, 27]", "17 Mar 96 14:58> [~invname, \"Name already exists.\", 0]", "17 Mar 96 14:58> ['method, 'valid_name, $user_db, $registry, 23]", "17 Mar 96 14:58> [~invname, 'valid_name, $user_db, $user_db, 5]", "17 Mar 96 14:58> [~invname, 'set_name, #1064, $user, 7]", "17 Mar 96 14:58> [~invname, 'create_user, $sys, $sys, 9]", "17 Mar 96 14:58> [~invname, 'connect_guest_cmd, $login_interface_3132, $login_interface, 27]", "18 Mar 96 17:05> [~invname, \"Name already exists.\", 0]", "18 Mar 96 17:05> ['method, 'valid_name, $user_db, $registry, 23]", "18 Mar 96 17:05> [~invname, 'valid_name, $user_db, $user_db, 5]", "18 Mar 96 17:05> [~invname, 'set_name, #1136, $user, 7]", "18 Mar 96 17:05> [~invname, 'create_user, $sys, $sys, 9]", "18 Mar 96 17:05> [~invname, 'connect_guest_cmd, $login_interface_3165, $login_interface, 27]", "23 Mar 96 13:20> [~error, \"The name 'user_charles is already taken.\", 0]", "23 Mar 96 13:20> ['function, 'set_objname]", "23 Mar 96 13:20> [~error, 'set_objname, #1503, $root, 16]", "23 Mar 96 13:20> [~error, 'set_objname, #1503, $user, 5]", "23 Mar 96 13:20> [~error, 'set_name, #1503, $user, 15]", "23 Mar 96 13:20> [~error, 'create_user, $sys, $sys, 9]", "23 Mar 96 13:20> [~error, 'connect_guest_cmd, $login_interface_3255, $login_interface, 27]", "24 Mar 96 17:28> [~invname, \"Name already exists.\", 0]", "24 Mar 96 17:28> ['method, 'valid_name, $user_db, $registry, 23]", "24 Mar 96 17:28> [~invname, 'valid_name, $user_db, $user_db, 5]", "24 Mar 96 17:28> [~invname, 'set_name, #1570, $user, 7]", "24 Mar 96 17:28> [~invname, 'create_user, $sys, $sys, 9]", "24 Mar 96 17:28> [~invname, 'connect_guest_cmd, $login_interface_3281, $login_interface, 27]", "25 Mar 96 17:10> [~error, \"The name 'user_mikeobrien is already taken.\", 0]", "25 Mar 96 17:10> ['function, 'set_objname]", "25 Mar 96 17:10> [~error, 'set_objname, #1643, $root, 16]", "25 Mar 96 17:10> [~error, 'set_objname, #1643, $user, 5]", "25 Mar 96 17:10> [~error, 'set_name, #1643, $user, 15]", "25 Mar 96 17:10> [~error, 'create_user, $sys, $sys, 9]", "25 Mar 96 17:10> [~error, 'connect_guest_cmd, $login_interface_3300, $login_interface, 27]", "25 Mar 96 17:10> [~invname, \"Name already exists.\", 0]", "25 Mar 96 17:10> ['method, 'valid_name, $user_db, $registry, 23]", "25 Mar 96 17:10> [~invname, 'valid_name, $user_db, $user_db, 5]", "25 Mar 96 17:10> [~invname, 'set_name, #1644, $user, 7]", "25 Mar 96 17:10> [~invname, 'create_user, $sys, $sys, 9]", "25 Mar 96 17:10> [~invname, 'connect_guest_cmd, $login_interface_3300, $login_interface, 27]", "26 Mar 96 09:59> [~invname, \"Name already exists.\", 0]", "26 Mar 96 09:59> ['method, 'valid_name, $user_db, $registry, 23]", "26 Mar 96 09:59> [~invname, 'valid_name, $user_db, $user_db, 5]", "26 Mar 96 09:59> [~invname, 'set_name, #1681, $user, 7]", "26 Mar 96 09:59> [~invname, 'create_user, $sys, $sys, 9]", "26 Mar 96 09:59> [~invname, 'connect_guest_cmd, $login_interface_3310, $login_interface, 27]", "26 Mar 96 15:56> [~invname, \"Name already exists.\", 0]", "26 Mar 96 15:56> ['method, 'valid_name, $user_db, $registry, 23]", "26 Mar 96 15:56> [~invname, 'valid_name, $user_db, $user_db, 5]", "26 Mar 96 15:56> [~invname, 'set_name, #1804, $user, 7]", "26 Mar 96 15:56> [~invname, 'create_user, $sys, $sys, 9]", "26 Mar 96 15:56> [~invname, 'connect_guest_cmd, $login_interface_3316, $login_interface, 27]", "01 Apr 96 11:24> [~error, \"The name 'user_loki is already taken.\", 0]", "01 Apr 96 11:24> ['function, 'set_objname]", "01 Apr 96 11:24> [~error, 'set_objname, #2159, $root, 16]", "01 Apr 96 11:24> [~error, 'set_objname, #2159, $user, 5]", "01 Apr 96 11:24> [~error, 'set_name, #2159, $user, 15]", "01 Apr 96 11:24> [~error, 'create_user, $sys, $sys, 9]", "01 Apr 96 11:24> [~error, 'connect_guest_cmd, $login_interface_3461, $login_interface, 27]", "01 Apr 96 12:42> [~invname, \"Name already exists.\", 0]", "01 Apr 96 12:42> ['method, 'valid_name, $user_db, $registry, 23]", "01 Apr 96 12:42> [~invname, 'valid_name, $user_db, $user_db, 5]", "01 Apr 96 12:42> [~invname, 'set_name, #2163, $user, 7]", "01 Apr 96 12:42> [~invname, 'create_user, $sys, $sys, 9]", "01 Apr 96 12:42> [~invname, 'connect_guest_cmd, $login_interface_3462, $login_interface, 27]", "09 Apr 96 01:17> [~invname, \"Name already exists.\", 0]", "09 Apr 96 01:17> ['method, 'valid_name, $user_db, $registry, 23]", "09 Apr 96 01:17> [~invname, 'valid_name, $user_db, $user_db, 5]", "09 Apr 96 01:17> [~invname, 'set_name, #2421, $user, 7]", "09 Apr 96 01:17> [~invname, 'create_user, $sys, $sys, 9]", "09 Apr 96 01:17> [~invname, 'connect_guest_cmd, $login_interface_3549, $login_interface, 27]", "09 Apr 96 19:27> [~error, \"The name 'user_scooter is already taken.\", 0]", "09 Apr 96 19:27> ['function, 'set_objname]", "09 Apr 96 19:27> [~error, 'set_objname, #2464, $root, 16]", "09 Apr 96 19:27> [~error, 'set_objname, #2464, $user, 5]", "09 Apr 96 19:27> [~error, 'set_name, #2464, $user, 15]", "09 Apr 96 19:27> [~error, 'create_user, $sys, $sys, 9]", "09 Apr 96 19:27> [~error, 'connect_guest_cmd, $login_interface_3565, $login_interface, 27]", "10 Apr 96 11:16> [~error, \"The name 'user_mikeobrien is already taken.\", 0]", "10 Apr 96 11:16> ['function, 'set_objname]", "10 Apr 96 11:16> [~error, 'set_objname, #2541, $root, 16]", "10 Apr 96 11:16> [~error, 'set_objname, #2541, $user, 5]", "10 Apr 96 11:16> [~error, 'set_name, #2541, $user, 15]", "10 Apr 96 11:16> [~error, 'create_user, $sys, $sys, 9]", "10 Apr 96 11:16> [~error, 'connect_guest_cmd, $login_interface_3576, $login_interface, 27]", "10 Apr 96 11:17> [~invname, \"Name already exists.\", 0]", "10 Apr 96 11:17> ['method, 'valid_name, $user_db, $registry, 23]", "10 Apr 96 11:17> [~invname, 'valid_name, $user_db, $user_db, 5]", "10 Apr 96 11:17> [~invname, 'set_name, #2544, $user, 7]", "10 Apr 96 11:17> [~invname, 'create_user, $sys, $sys, 9]", "10 Apr 96 11:17> [~invname, 'connect_guest_cmd, $login_interface_3576, $login_interface, 27]", "13 Apr 96 23:55> [~invname, \"Names must have at least 3 alpha-numeric characters in them\", 0]", "13 Apr 96 23:55> ['method, 'valid_name, $user_db, $registry, 16]", "13 Apr 96 23:55> [~invname, 'valid_name, $user_db, $user_db, 5]", "13 Apr 96 23:55> [~invname, 'set_name, #2661, $user, 7]", "13 Apr 96 23:55> [~invname, 'create_user, $sys, $sys, 9]", "13 Apr 96 23:55> [~invname, 'connect_guest_cmd, $login_interface_3629, $login_interface, 27]", "24 Apr 96 13:53> [~invname, \"Name already exists.\", 0]", "24 Apr 96 13:53> ['method, 'valid_name, $user_db, $registry, 23]", "24 Apr 96 13:53> [~invname, 'valid_name, $user_db, $user_db, 5]", "24 Apr 96 13:53> [~invname, 'set_name, #1147, $user, 7]", "24 Apr 96 13:53> [~invname, 'create_user, $sys, $sys, 9]", "24 Apr 96 13:53> [~invname, 'connect_guest_cmd, $login_interface_3890, $login_interface, 27]", "01 May 96 21:00> [~invname, \"Names must have at least 3 alpha-numeric characters in them\", 0]", "01 May 96 21:00> ['method, 'valid_name, $user_db, $registry, 16]", "01 May 96 21:00> [~invname, 'valid_name, $user_db, $user_db, 5]", "01 May 96 21:00> [~invname, 'set_name, #1105, $user, 7]", "01 May 96 21:00> [~invname, 'create_user, $sys, $sys, 9]", "01 May 96 21:00> [~invname, 'connect_guest_cmd, $login_interface_4091, $login_interface, 27]", "09 May 96 20:23> [~perm, \"$guest_jennertest is not one of: $list\", #900]", "09 May 96 20:23> ['method, 'perms, $user_db, $root, 26]", "09 May 96 20:23> [~perm, 'insert, $user_db, $db, 5]", "09 May 96 20:23> [~methoderr, 'insert, $user_db, $registry, 7]", "09 May 96 20:23> [~methoderr, 'init_user, #900, $user, 9]", "09 May 96 20:23> [~methoderr, '_initialize_ancestor, #900, $root, 8]", "09 May 96 20:23> [~methoderr, 'initialize, #900, $root, 10]", "09 May 96 20:23> [~methoderr, 'create, $sys, $sys, 8]", "09 May 96 20:23> [~methoderr, 'spawn_sender, $sys, $sys, 8]", "09 May 96 20:23> [~methoderr, 'spawn, $guest, $root, 36]", "09 May 96 20:23> [~methoderr, 'create_user, $sys, $sys, 8]", "09 May 96 20:23> [~methoderr, 'connect_guest_cmd, $login_interface_4251, $login_interface, 27]", "09 May 96 20:25> [~perm, \"$guest_jennertest is not one of: $list\", #903]", "09 May 96 20:25> ['method, 'perms, $user_db, $root, 26]", "09 May 96 20:25> [~perm, 'insert, $user_db, $db, 5]", "09 May 96 20:25> [~methoderr, 'insert, $user_db, $registry, 7]", "09 May 96 20:25> [~methoderr, 'init_user, #903, $user, 9]", "09 May 96 20:25> [~methoderr, '_initialize_ancestor, #903, $root, 8]", "09 May 96 20:25> [~methoderr, 'initialize, #903, $root, 10]", "09 May 96 20:25> [~methoderr, 'create, $sys, $sys, 8]", "09 May 96 20:25> [~methoderr, 'spawn_sender, $sys, $sys, 8]", "09 May 96 20:25> [~methoderr, 'spawn, $guest, $root, 36]", "09 May 96 20:25> [~methoderr, 'create_user, $sys, $sys, 8]", "09 May 96 20:25> [~methoderr, 'connect_guest_cmd, $login_interface_4252, $login_interface, 27]", "09 May 96 20:28> [~perm, \"Permission Denied: $guest_miro is not a writer.\", #906]", "09 May 96 20:28> ['method, 'perms, $user_db, $root, 22]", "09 May 96 20:28> [~perm, 'insert, $user_db, $db, 5]", "09 May 96 20:28> [~methoderr, 'insert, $user_db, $registry, 7]", "09 May 96 20:28> [~methoderr, 'init_user, #906, $user, 9]", "09 May 96 20:28> [~methoderr, '_initialize_ancestor, #906, $root, 8]", "09 May 96 20:28> [~methoderr, 'initialize, #906, $root, 10]", "09 May 96 20:28> [~methoderr, 'create, $sys, $sys, 8]", "09 May 96 20:28> [~methoderr, 'spawn_sender, $sys, $sys, 8]", "09 May 96 20:28> [~methoderr, 'spawn, $guest, $root, 36]", "09 May 96 20:28> [~methoderr, 'create_user, $sys, $sys, 8]", "09 May 96 20:28> [~methoderr, 'connect_guest_cmd, $login_interface_4253, $login_interface, 27]", "19 May 96 21:17> [~invname, \"`the' is not allowed as part of a name.\", 0]", "19 May 96 21:17> ['method, 'valid_name, $user_db, $registry, 33]", "19 May 96 21:17> [~invname, 'valid_name, $user_db, $user_db, 5]", "19 May 96 21:17> [~invname, 'set_name, #1110, $user, 7]", "19 May 96 21:17> [~invname, 'create_user, $sys, $sys, 9]", "19 May 96 21:17> [~invname, 'connect_guest_cmd, $login_interface_4498, $login_interface, 27]", "26 May 96 12:49> [~invname, \"Names must have at least 3 alpha-numeric characters in them\", 0]", "26 May 96 12:49> ['method, 'valid_name, $user_db, $registry, 16]", "26 May 96 12:49> [~invname, 'valid_name, $user_db, $user_db, 5]", "26 May 96 12:49> [~invname, 'set_name, #1467, $user, 7]", "26 May 96 12:49> [~invname, 'create_user, $sys, $sys, 9]", "26 May 96 12:49> [~invname, 'connect_guest_cmd, $login_interface_4635, $login_interface, 27]", "01 Jun 96 17:07> [~error, \"The name 'user_nip is already taken.\", 0]", "01 Jun 96 17:07> ['function, 'set_objname]", "01 Jun 96 17:07> [~error, 'set_objname, #1170, $root, 16]", "01 Jun 96 17:07> [~error, 'set_objname, #1170, $user, 5]", "01 Jun 96 17:07> [~error, 'set_name, #1170, $user, 15]", "01 Jun 96 17:07> [~error, 'create_user, $sys, $sys, 9]", "01 Jun 96 17:07> [~error, 'connect_guest_cmd, $login_interface_4753, $login_interface, 27]", "03 Jun 96 16:01> [~invname, \"Name already exists.\", 0]", "03 Jun 96 16:01> ['method, 'valid_name, $user_db, $registry, 23]", "03 Jun 96 16:01> [~invname, 'valid_name, $user_db, $user_db, 5]", "03 Jun 96 16:01> [~invname, 'set_name, #1281, $user, 7]", "03 Jun 96 16:01> [~invname, 'create_user, $sys, $sys, 9]", "03 Jun 96 16:01> [~invname, 'connect_guest_cmd, $login_interface_4780, $login_interface, 27]", "03 Jun 96 16:01> [~invname, \"Names must have at least 3 alpha-numeric characters in them\", 0]", "03 Jun 96 16:01> ['method, 'valid_name, $user_db, $registry, 16]", "03 Jun 96 16:01> [~invname, 'valid_name, $user_db, $user_db, 5]", "03 Jun 96 16:01> [~invname, 'set_name, #1282, $user, 7]", "03 Jun 96 16:01> [~invname, 'create_user, $sys, $sys, 9]", "03 Jun 96 16:01> [~invname, 'connect_guest_cmd, $login_interface_4780, $login_interface, 27]", "03 Jun 96 16:02> [~perm, \"Can't specify a numeric suffix.\", 0]", "03 Jun 96 16:02> ['method, 'spawn, $guest, $root, 18]", "03 Jun 96 16:02> [~perm, 'create_user, $sys, $sys, 8]", "03 Jun 96 16:02> [~perm, 'connect_guest_cmd, $login_interface_4780, $login_interface, 27]", "06 Jun 96 15:31> [~invname, \"Name already exists.\", 0]", "06 Jun 96 15:31> ['method, 'valid_name, $user_db, $registry, 23]", "06 Jun 96 15:31> [~invname, 'valid_name, $user_db, $user_db, 5]", "06 Jun 96 15:31> [~invname, 'set_name, #1391, $user, 7]", "06 Jun 96 15:31> [~invname, 'create_user, $sys, $sys, 9]", "06 Jun 96 15:31> [~invname, 'connect_guest_cmd, $login_interface_4824, $login_interface, 27]", "06 Jun 96 15:36> [~invname, \"Name already exists.\", 0]", "06 Jun 96 15:36> ['method, 'valid_name, $user_db, $registry, 23]", "06 Jun 96 15:36> [~invname, 'valid_name, $user_db, $user_db, 5]", "06 Jun 96 15:36> [~invname, 'set_name, #1395, $user, 7]", "06 Jun 96 15:36> [~invname, 'create_user, $sys, $sys, 9]", "06 Jun 96 15:36> [~invname, 'connect_guest_cmd, $login_interface_4825, $login_interface, 27]", "07 Jun 96 09:26> [~invname, \"Name already exists.\", 0]", "07 Jun 96 09:26> ['method, 'valid_name, $user_db, $registry, 23]", "07 Jun 96 09:26> [~invname, 'valid_name, $user_db, $user_db, 5]", "07 Jun 96 09:26> [~invname, 'set_name, #1516, $user, 7]", "07 Jun 96 09:26> [~invname, 'create_user, $sys, $sys, 9]", "07 Jun 96 09:26> [~invname, 'connect_guest_cmd, $login_interface_4873, $login_interface, 28]", "07 Jun 96 09:26> [~invname, \"Name already exists.\", 0]", "07 Jun 96 09:26> ['method, 'valid_name, $user_db, $registry, 23]", "07 Jun 96 09:26> [~invname, 'valid_name, $user_db, $user_db, 5]", "07 Jun 96 09:26> [~invname, 'set_name, #1517, $user, 7]", "07 Jun 96 09:26> [~invname, 'create_user, $sys, $sys, 9]", "07 Jun 96 09:26> [~invname, 'connect_guest_cmd, $login_interface_4873, $login_interface, 28]"];
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;
var $root managed = [$login_log];
var $root owners = [$login_log];
var $root owned = [$login_log];


new object $generic_map: $note;

var $root manager = $generic_map;
var $located obvious = 1;
var $root flags = ['variables, 'methods, 'code, 'core];
var $root created_on = 840768594;
var $root owners = [$generic_map];
var $located location = $nowhere;
var $text text = [];
var $root inited = 1;
var $described prose = <$j_ctext_frob, [["This is generic map object - it's generic holder for area maps."], #[['this, $generic_map]]]>;
var $has_commands shortcuts = #[];
var $has_commands remote = #[];
var $has_commands local = #[];
var $gendered gender = $gender_neuter;
var $named name = ['uniq, "Generic Map", "the Generic Map"];
var $named name_aliases = [];
var $has_settings defined_settings = #[];
var $has_settings local_settings = #[];
var $has_settings settings = #[];
var $root child_index = 1;
var $root managed = [$generic_map];
var $root owned = [$generic_map];

public method .view() {
    arg x, y, code, height, width;
    var text, i, out, pos, line;
    
    text = .text();
    pos = 0;
    out = [];
    for i in [y .. y + height] {
        line = (| ((text[i]).subrange(x + 1)).pad(width) |) || "";
        pos = pos || (code in line);
        out += [((line.left(width)).replace(code, "**")).sed("[0-9]", " ", "g")];
    }
    return [@out, pos ? "Your position is '**'." : "Your position is not shown on this map."];
    
    // $#Edited: 25 Aug 96 19:08 $jenner
};


new object $map_of_taobh_thiar: $generic_map;

var $root manager = $map_of_taobh_thiar;
var $root flags = ['methods, 'code, 'core];
var $root created_on = 841020994;
var $root owners = [$map_of_taobh_thiar];
var $text text = ["", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "                                     ^", "                                     |", "                                  The Pit", "                                    00"];
var $has_settings defined_settings = #[];
var $has_settings local_settings = #[];
var $has_settings settings = #[];
var $named name = ['uniq, "Map of Taobh Thiar", "the Map of Taobh Thiar"];
var $named name_aliases = [];
var $gendered gender = $gender_neuter;
var $has_commands shortcuts = #[];
var $has_commands remote = #[];
var $has_commands local = #[];
var $described prose = <$j_ctext_frob, [["This map shows the Taobh Thiar."], #[['this, $map_of_taobh_thiar]]]>;
var $located location = $nowhere;
var $located obvious = 1;
var $public public = ['readable];
var $root inited = 1;
var $root managed = [$map_of_taobh_thiar];
var $root owned = [$map_of_taobh_thiar];


new object $mail_message: $text, $mail_root;

var $root child_index = 219;
var $root fertile = 1;
var $root inited = 1;
var $root created_on = 796268969;
var $root managed = [$mail_message];
var $root flags = ['methods, 'code, 'fertile, 'core, 'variables];
var $root quota_exempt = 1;
var $mail_message readers = [];
var $mail_message header = #[];
var $mail_message delivered = 0;
var $root manager = $mail_message;
var $root owners = [$mail_message];
var $root owned = [$mail_message];

public method .del_recipient(): nooverride {
    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 method .add_recipient(): nooverride {
    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 method .letter(): nooverride {
    if (!($mail_lib.has_mail_perms(caller())))
        (> .perms(sender()) <);
    return (.header()).add('text, .text());
};

public method .readers(): nooverride {
    if (!($mail_lib.has_mail_perms(caller())))
        (> .perms(sender()) <);
    return readers;
};

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

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

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

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

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

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

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

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

public method .subject(): nooverride {
    return (| header['subj] |) || "<none>";
};

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

public method .format(): nooverride {
    var output, names, o, h;
    
    if (!($mail_lib.has_mail_perms(caller())))
        (> .perms(sender()) <);
    h = .header();
    output = ["From:    " + ($object_lib.get_name(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;
    
    // $# Edited 23 Oct 1995 12:10 Lynx ($lynx)
};

public method .header(): nooverride {
    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 method .uninit_mail_message() {
    var r;
    
    for r in (.recipients())
        (| r.del_mail(this(), (| (.from()) || this() |)) |);
    header = #[];
    readers = [];
};

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

public method .has_read(): nooverride {
    arg who;
    
    return who in readers;
};

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

public method .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.mmap('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 method .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;
};


new object $itext: $text;

var $root child_index = 2;
var $root fertile = 1;
var $root inited = 1;
var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'fertile, 'variables, 'core];
var $text text = ["hello", "this", "world"];
var $itext all_text = #[["foo", [["hello", "world"], ""]], ["bar", [["hello", "this", "world"], ""]]];
var $itext current = "foo";
var $root manager = $itext;
var $root managed = [$itext];
var $root owners = [$itext];
var $root owned = [$itext];

public method .current() {
    return current;
};

public method .topics() {
    return all_text.keys();
};

public method .set_desc() {
    arg desc, [topic];
    
    // set the description for a topic
    // The description is a short text string meant for use in an index
    // if <topic> is not given assume current
    topic = [@topic, current][1];
    if (topic in (all_text.keys()))
        all_text = all_text.add(topic, (all_text[topic]).replace(2, short_desc));
    else
        throw(~topicnf, ("Topic " + topic) + " not found.");
};

public method .store() {
    arg [topic];
    
    topic = [@topic, current][1];
    if (!(topic in (all_text.keys())))
        all_text = all_text.add(topic, [.text(), ""]);
    all_text = all_text.add(topic, (all_text[topic]).replace(1, .text()));
};

public method .set_current() {
    arg topic;
    
    current = topic;
    if (topic in (all_text.keys()))
        .set_text((all_text[topic])[1]);
    else
        .set_text([]);
};

public method .get_topic() {
    arg topic, [who];
    
    if (topic in (all_text.keys()))
        return (all_text[topic])[1];
    else
        throw(~topicnf, ("Topic " + topic) + " not found.");
};

root method .init_itext() {
    current = "";
    all_text = #[];
};

public method .get_desc() {
    arg topic, [who];
    
    if (topic in (all_text.keys()))
        return (all_text[topic])[2];
    else
        throw(~topicnf, ("Topic " + topic) + " not found.");
};


new object $mail_lib: $libraries, $mail_root;

var $root inited = 1;
var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'core, 'variables];
var $mail_lib mail_system = [$mail_message, $mail_list, $mail_ui, $mail_lib];
var $root manager = $mail_lib;
var $root managed = [$mail_lib];
var $root owners = [$mail_lib];
var $root owned = [$mail_lib];

public method .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.name()).replace(" ", "-");
    return "*" + ((obj.name()).replace(" ", "-"));
    
    // $# Edited 29 Oct 1995 12:56 Lynx ($lynx)
};

public method .has_mail_perms() {
    arg [args];
    var obj;
    
    for obj in (args) {
        if ((!(obj in mail_system)) && (!($sys.is_system(obj))))
            return 0;
    }
};

public method .match_mail_recipient() {
    arg name;
    
    catch ~namenf {
        if ((name[1]) == "*")
            return $mail_db.search(name.subrange(2));
        if (name == "me")
            return sender();
        return $user_db.search(name);
    } with {
        throw(~listnf, ("No mail recipient found by the name \"" + name) + "\".");
    }
};

public method .range_to_actual() {
    arg rng, current;
    var start, end, out, listm, m, x, list;
    
    list = current['list];
    listm = list.mail();
    if (type(rng[1]) == 'integer) {
        start = rng[1];
    } else {
        switch (rng[1]) {
            case 'end:
                if (type(rng[2]) != 'symbol)
                    throw(~range, "Backwards range.");
                start = ((current['list]).mail()).length();
            case 'start:
                start = 1;
            case 'current:
                start = (current['location]) in listm;
            case 'specific:
                out = [];
                for m in ((rng[2]).explode_english_list()) {
                    if ((!(x = toint(m))) || (x < 1)) {
                        sender().tell(("Ignoring list range element '" + m) + "'.");
                    } else {
                        catch ~range
                            out = setadd(out, (> listm[x] <));
                        with
                            sender().tell(((((("List range #" + x) + " is higher than the messages in ") + (list.mail_name())) + " (") + (listm.length())) + ")");
                    }
                }
                return out || throw(~range, "No elements in list range.");
        }
    }
    if (type(rng[2]) == 'integer) {
        end = rng[2];
    } else {
        switch (rng[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());
        }
    }
    if (start > end)
        throw(~range, "Backwards range.");
    out = [];
    for m in [start .. end] {
        catch ~range
            out = setadd(out, (> listm[m] <));
        with
            sender().tell(((((("List range #" + m) + " is higher than the messages in ") + (list.mail_name())) + " (") + (listm.length())) + ")");
    }
    return out || throw(~range, "No elements in list range.");
};


new object $user_parsers: $misc;

var $root child_index = 11;
var $root fertile = 1;
var $root trusted = [];
var $root inited = 1;
var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'fertile, 'core, 'variables];
var $root manager = $user_parsers;
var $root managed = [$user_parsers];
var $root owners = [$user_parsers];
var $root owned = [$user_parsers];

public method .parse() {
    arg user, str, next_parser, [other_parsers];
    
    // Minimum parser routine.
    return next_parser.parse(user, str, @other_parsers);
};


new object $null_parser: $user_parsers;

var $root inited = 1;
var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'variables, 'core];
var $root manager = $null_parser;
var $root managed = [$null_parser];
var $root owners = [$null_parser];
var $root owned = [$null_parser];

public method .parse() {
    arg user, str, [anything_else];
    var i;
    
    for i in [1 .. str.length()] {
        if ((str[i]) != " ")
            return ['failed];
    }
    return ['ok];
};


new object $command_aliases_parser: $user_parsers;

var $root trusted = [];
var $root inited = 1;
var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'variables, 'core];
var $root manager = $command_aliases_parser;
var $root managed = [$command_aliases_parser];
var $root owners = [$command_aliases_parser];
var $root owned = [$command_aliases_parser];

public method .parse() {
    arg user, str, next_parser, [other_parsers];
    var alias, match, i, result, old;
    
    for alias in (user.command_aliases()) {
        match = str.match_pattern(alias[1]);
        if (match != 0) {
            old = str;
            str = "";
            for i in (alias[2]) {
                if (type(i) == 'integer)
                    str = str + (match[i]);
                else
                    str = str + i;
            }
        }
    }
    result = next_parser.parse(user, str, @other_parsers);
    if (old && (result == 'failed))
        return ['error, ("Command converted to \"" + str) + "\" but not understood."];
    return result;
};


new object $command_parser: $user_parsers;

var $root manager = $command_parser;
var $root created_on = 796680318;
var $root inited = 1;
var $root flags = ['methods, 'code, 'core, 'variables];
var $root managed = [$command_parser];
var $root owners = [$command_parser];
var $root owned = [$command_parser];

public method .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)).join()) || ""];
        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_local_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) |))) {
            if ((c[1]) == 'remote)
                return .remote(u, @c[2]);
            p = p + (c[2]);
        }
        for obj in ((| (u.location()).exits() |) || []) {
            if (obj.match_name(str))
                return ['command, obj, 'invoke];
        }
        if (p)
            return .partial(u, cmd, p);
    }
    return next_parser.parse(u, str, @other_parsers);
    
    // $#Edited: 12 Aug 96 18:29 $jenner
};

public method .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 ['error, (line + (templates.to_english("", " or "))) + "."];
    
    // $# Edited 18 Oct 1995 11:55 Lynx ($lynx)
};

public method .shortcut() {
    arg user, method, parsed;
    
    return ['command, user, method, @parsed];
};

public method .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 method .grasp_for_remote_command() {
    arg str, cmd, args;
    var reg, obj, cdef, match, matched, info;
    
    reg = args.match_regexp("[$#][a-z_0-9][a-z_0-9]*");
    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 = args.match_template(cdef[2]);
        if (match != 0)
            matched = matched + [[match.length(), obj, [str, cmd, @match], @cdef.subrange(3)]];
    }
    if (matched) {
        matched = matched.msort(matched.slice(1));
        return ['remote, matched];
    }
    return ['partial, [[str, cmd], info.slice(3)]];
    
    // $#Edited: 12 Aug 96 18:29 $jenner
};

public method .local() {
    arg user, [matches];
    var parsed, match;
    
    parsed = [];
    for match in (matches) {
        match = ._local(user, @match);
        if ((match[1]) == 'command)
            return match;
        parsed = [match[2]] + parsed;
    }
    return ['error, parsed.compress()];
};

public method .remote() {
    arg user, [matches];
    var parsed, match;
    
    parsed = [];
    for match in (matches) {
        match = ._remote(user, @match);
        if ((match[1]) == 'command)
            return match;
        parsed = [match[2]] + parsed;
    }
    return ['error, parsed.compress()];
    
    // $#Edited: 15 Jun 96 17:55 $levi
};

public method .handle_error() {
    arg traceback;
    
    return (traceback[1])[2];
};

public method ._remote() {
    arg user, nmatch, definer, match, template, method, info;
    var x, value, that, cmd;
    
    cmd = match[2];
    catch any {
        for x in [1 .. nmatch] {
            if (dict_contains(info, x)) {
                if (((info[x])[1]) == 'this)
                    that = (> user.match_environment(match[x + 2]) <);
                else
                    value = (> .convert_arg(cmd, (info[x])[1], match[x + 2], user, ((info[x])[2]) || [definer], user) <);
                match = match.replace(x + 2, value);
            }
        }
    } with {
        if (error() == ~syntax)
            return ['error, (((traceback()[1])[2]) + template) + "\""];
        return ['error, (traceback()[1])[2]];
    }
    if (!that)
        return ['error, "An error was encountered: no target object found."];
    return ['command, that, method, @match];
};

public method ._local() {
    arg user, nmatch, match, template, method, info;
    var x, cmd;
    
    cmd = match[2];
    catch any {
        for x in [1 .. nmatch] {
            if (dict_contains(info, x))
                match = match.replace(x + 2, (> .convert_arg(cmd, (info[x])[1], match[x + 2], user, (info[x])[2], user) <));
        }
    } with {
        if (error() == ~syntax)
            return ['error, (((traceback()[1])[2]) + template) + "\""];
        return ['error, (traceback()[1])[2]];
    }
    return ['command, user, method, @match];
};

public method .convert_arg() {
    arg cmd, type, str, me, argargs, target;
    var obj, args, anc, out, x, y, list;
    
    switch (type) {
        case 'list:
            out = [];
            if (!str)
                return [(> .convert_arg(cmd, argargs[1], "", me, argargs[2], target) <)];
    
            // or do the whole list
            if ("," in str)
                list = str.explode_english_list(",");
            else
                list = explode(str);
            for x in (list) {
                catch ~ambig {
                    out = out.setadd(.convert_arg(cmd, argargs[1], x, me, argargs[2], target));
                } with {
                    if ((| (traceback()[1])[3] |) && (!((argargs[1]) in ['user, 'user_opt]))) {
                        for y in ((traceback()[1])[3])
                            out = out.setadd((> .convert_arg(cmd, argargs[1], tostr(y), me, argargs[2], target) <));
                    } else {
                        rethrow(error());
                    }
                }
            }
            return out;
        case 'any:
            return str;
        case 'any_opt:
            args = $parse_lib.getopt(str, argargs);
            return args;
        case 'object:
            return (> me.match_environment(str) <);
        case 'object_opt:
            args = $parse_lib.getopt(str, argargs);
            if (!(args[1]))
                throw(~syntax, "No reference specified for command \"");
            obj = (> me.match_environment((args[1])[1]) <);
            return [obj, delete(args[1], 1), args[2]];
        case 'objref:
            return (> $parse_lib.ref(str, me) <);
        case 'objref_opt:
            args = $parse_lib.getopt(str, argargs);
            if (!(args[1]))
                throw(~syntax, "No reference specified for command \"");
            obj = (> $parse_lib.ref((args[1])[1], me) <);
            return [obj, delete(args[1], 1), args[2]];
        case 'user:
            if (str == "me")
                return me;
            return (> $user_db.search(str) <);
        case 'user_opt:
            args = $parse_lib.getopt(str, argargs);
            if (!(args[1]))
                throw(~syntax, "Nobody specified for command \"");
            if (str == "me")
                return me;
            return [(> $user_db.search((args[1])[1]) <), args[2]];
        case 'number:
            return (> str.to_number() <);
        case 'number_opt:
            args = $parse_lib.getopt(str, argargs);
            if (!(args[1]))
                throw(~syntax, "No number specified for command \"");
            return [(> str.to_number() <), args[2]];
        case 'descendant:
            obj = (> me.match_environment(str) <);
            anc = argargs ? argargs[1] : user;
            if (!(obj.has_ancestor(anc)))
                throw(~parse, strfmt("You cannot %s %s because it is not %s!", cmd, obj.name(), anc.name()));
            return obj;
        case 'descendant_opt:
            args = $parse_lib.getopt(str, argargs);
            if (!(args[1]))
                throw(~syntax, "No descendant specified for command \"");
            obj = (> me.match_environment((args[1])[1]) <);
            anc = argargs ? argargs[1] : user;
            if (!(obj.has_ancestor(anc)))
                throw(~parse, strfmt("You cannot %s %s because it is not %s!", cmd, obj.name(), anc.name()));
            return [obj, args[2]];
        default:
            throw(~ack, ("Support for the type '" + type) + " is incomplete!");
    }
};


new object $conference_parser: $user_parsers;

var $root trusted = [];
var $root inited = 1;
var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'variables, 'core];
var $root manager = $conference_parser;
var $root managed = [$conference_parser];
var $root owners = [$conference_parser];
var $root owned = [$conference_parser];

public method .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);
};


new object $editor_parser: $user_parsers;

var $root manager = $editor_parser;
var $root flags = ['methods, 'code, 'core, 'variables];
var $root created_on = 820684599;
var $root owners = [$editor_parser];
var $root inited = 1;
var $editor_parser cmdlist = #[["'", 'insert_cmd], ["i", 'insert_cmd], ["a", 'eappend_cmd], ["d", 'delete_cmd], ["l", 'elist_cmd], ["s", 'subst_cmd], [".", 'line_cmd], ["save", 'save_cmd], ["abort", 'abort_cmd], ["done", 'done_cmd], ["fill", 'fill_cmd], ["help", 'help_cmd], [",", 'eappend_cmd], ["enter", 'enter_cmd], ["list", 'elist_cmd]];
var $root managed = [$editor_parser];
var $root owned = [$editor_parser];

public method .parse() {
    arg u, str, next_parser, [other_parsers];
    var n, cmd, argstr;
    
    while (str && ((str[1]) == " "))
        str = str.subrange(2);
    if (!str)
        return next_parser.parse(u, str, @other_parsers);
    if ((str[1]) == ">")
        return next_parser.parse(u, str.subrange(2), @other_parsers);
    if (((str[1]) in ["'", ",", "."]) || (((str.length()) > 1) && (((str[1]) == "s") && ((str[2]) in ($string.non_alphanumeric()))))) {
        cmd = str[1];
        argstr = str.subrange(2);
    } else if (!(n = " " in str)) {
        cmd = str;
        argstr = "";
    } else {
        cmd = str.subrange(1, n - 1);
        argstr = str.subrange(n + 1);
    }
    if (!(| (n = cmdlist[cmd]) |))
        return next_parser.parse(u, str, @other_parsers);
    return ['command, u.active_editor(), n, argstr];
    
    // $#Edited: 24 Aug 96 16:56 $jenner
};


new object $channel_parser: $user_parsers;

var $root manager = $channel_parser;
var $root flags = ['variables, 'methods, 'code, 'core];
var $root created_on = 838745026;
var $root owners = [$channel_parser];
var $root inited = 1;
var $root managed = [$channel_parser];
var $root owned = [$channel_parser];

public method .parse() {
    arg user, str, next_parser, [other_parsers];
    var cname, first_word, parse_list;
    
    if (!str)
        return next_parser.parse(user, str, @other_parsers);
    parse_list = str.explode();
    first_word = parse_list[1];
    cname = user.channel_alias(first_word);
    if ((cname != "") && ((parse_list.length()) > 1))
        return ['command, user, 'broadcast, cname, str.subrange((first_word.length()) + 2)];
    return next_parser.parse(user, str, @other_parsers);
    
    // $#Edited: 22 May 96 18:16 Chuck ($user_chuck)
    // $#Edited: 11 Aug 96 03:57 $levi
    // $#Edited: 23 Aug 96 18:46 $user_chuck
};


new object $filters: $misc;

var $root child_index = 1;
var $root inited = 1;
var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'core, 'variables];
var $root manager = $filters;
var $root managed = [$filters];
var $root owners = [$filters];
var $root owned = [$filters];

public method .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;
};


new object $wrap_filter: $filters;

var $root inited = 1;
var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'variables, 'core];
var $root manager = $wrap_filter;
var $root managed = [$wrap_filter];
var $root owners = [$wrap_filter];
var $root owned = [$wrap_filter];

public method .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;
};


new object $ctext_filter: $filters;

var $root manager = $ctext_filter;
var $root owners = [$ctext_filter];
var $root inited = 1;
var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'variables, 'core];
var $root managed = [$ctext_filter];
var $root owned = [$ctext_filter];

public method .filter_() {
    arg what, [defaults];
    var dic, output;
    
    dic = ([@defaults, #[]][1]).union(#[['receiver, sender()], ['time, 'post], ['formatter, $cml2_telnet_format]]);
    switch (class(what)) {
        case $j_ctext_frob:
            output = (what.set_var('receiver, sender())).format();
        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;
    
    // $#Edited: 18 Jul 96 20:16 $jenner
};


new object $lock_parser: $misc;

var $root inited = 1;
var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'core, 'variables];
var $root manager = $lock_parser;
var $root managed = [$lock_parser];
var $root owners = [$lock_parser];
var $root owned = [$lock_parser];

public method .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.");
        }
    }
};


new object $editor_session: $misc;

var $root manager = $editor_session;
var $root flags = ['methods, 'code, 'fertile, 'core, 'variables];
var $root created_on = 820684587;
var $root owners = [$editor_session];
var $root inited = 1;
var $root child_index = 269;
var $editor_session finisher = 0;
var $editor_session sender = 0;
var $editor_session text = 0;
var $editor_session line = 0;
var $editor_session modified = 0;
var $editor_session client_data = 0;
var $editor_session finisher_object = 0;
var $root managed = [$editor_session];
var $root owned = [$editor_session];

public method .startup() {
    arg fin_object, finish_method, initial_text, cdata;
    
    (> .perms(sender()) <);
    if (type(initial_text) == 'string)
        initial_text = [initial_text];
    sender = sender();
    finisher = finish_method;
    finisher_object = fin_object;
    text = initial_text;
    line = 1;
    modified = 0;
    client_data = cdata;
    if ((| (sender().local_editor()) == 'mcp |)) {
        sender.tell([(("#$# edit name: " + ((.name()).strip())) + " upload: @mcp-upload-session ") + (.name()), @text, "."]);
        text = 'mcp;
        sender.store_editor();
        return 0;
    } else {
        return 1;
    }
    
    // $#Edited: 06 Aug 96 16:47 $jenner
};

public method .abort_cmd() {
    arg [args];
    
    (> .perms(sender()) <);
    sender.quit_editor();
    return "Aborted. Editor cleared.";
    
    // $#Edited: 09 Dec 95 22:12 Miro ($miro)
    // $#Edited: 03 Jan 96 16:01 Jenner ($jenner)
};

public method .insert_cmd() {
    arg what;
    
    (> .perms(sender()) <);
    modified = 1;
    text = text.insert(line, what);
    line = line + 1;
    return ("Line " + tostr(line - 1)) + " added.";
    
    // $#Edited: 09 Dec 95 22:12 Miro ($miro)
    // $#Edited: 03 Jan 96 16:01 Jenner ($jenner)
};

public method .save_cmd() {
    arg [args];
    var err;
    
    (> .perms(sender()) <);
    if ((> (err = sender.do_save(finisher_object, finisher, text, client_data)) <) == 'clear) {
        sender.quit_editor();
        return "Done. Editor cleared.";
    }
    if (type(err) != 'list)
        modified = 0;
    return err ? err : "Save completed.";
    
    // $#Edited: 18 Aug 96 19:07 $jenner
};

public method .delete_cmd() {
    arg what;
    var start, end;
    
    (> .perms(sender()) <);
    catch any
        start = ._parse_range(what);
    with
        return "Illegal range, can't delete.";
    end = start[2];
    start = start[1];
    text = (text.subrange(1, start - 1)) + (text.subrange(end + 1));
    modified = 1;
    line = start;
    return (((("Deleted " + tostr((end - start) + 1)) + ((end == start) ? " line." : " lines.")) + " Current set to ") + tostr(line)) + ".";
    
    // $#Edited: 09 Dec 95 22:12 Miro ($miro)
    // $#Edited: 03 Jan 96 16:56 Jenner ($jenner)
};

public method .line_cmd() {
    arg number;
    
    (> .perms(sender()) <);
    number = (number == "$") ? text.length() : (number.to_number());
    if ((number < 1) || (number > ((text.length()) + 1)))
        return "Out if range.";
    else
        line = number;
    return ("Current set to " + tostr(line)) + ".";
    
    // $#Edited: 24 Aug 96 16:06 $jenner
};

public method .elist_cmd() {
    arg rangestr;
    var start, end, out, i, j, k, l, lineno;
    
    (> .perms(sender()) <);
    if (text == [])
        return "There is no text.";
    if (rangestr == "") {
        l = text.length();
        start = line - 7;
        end = line + 7;
        if (start < 1) {
            end = (end + 1) - start;
            if (end > l)
                end = l;
            start = 1;
        } else if (end > l) {
            start = (start - end) + l;
            end = l;
            if (start < 1)
                start = 1;
        }
        start = [start, end];
    } else {
        catch any
            start = ._parse_range(rangestr);
        with
            return "Illegal range, can't list that.";
    }
    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_lines(sender.linelen(), "         ")];
        j = j + 1;
    }
    if ((j == line) && (j == ((text.length()) + 1)))
        out = [@out, "".right(5, "^")];
    return out;
    
    // $#Edited: 09 Dec 95 22:12 Miro ($miro)
    // $#Edited: 03 Jan 96 16:01 Jenner ($jenner)
};

public method ._parse_range() {
    arg what;
    var start, end, range, l;
    
    l = text.length();
    what = what.replace("$", tostr(l));
    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 > l) || (end < start)))
        throw(~range, "Illegal range.");
    return [start, end];
    
    // $#Edited: 09 Dec 95 22:12 Miro ($miro)
    // $#Edited: 03 Jan 96 16:01 Jenner ($jenner)
};

public method .eappend_cmd() {
    arg what;
    
    (> .perms(sender()) <);
    modified = 1;
    if (line == 1) {
        text = text.insert(line, what);
        line = 2;
        return "Line 1 added.";
    } else {
        text = text.replace(line - 1, (text[line - 1]) + what);
        return ("Appended to line " + tostr(line - 1)) + ".";
    }
    
    // $#Edited: 09 Dec 95 22:12 Miro ($miro)
    // $#Edited: 03 Jan 96 16:01 Jenner ($jenner)
};

public method .help_cmd() {
    arg what;
    
    return ["Editor commands:", "  l [<range>]                          list range", "  . <line>                             insert from line", "  '<text>                              insert text", "  ,<text>                              append text", "  enter                                enter several lines of text", "  d [<range>]                          delete text", "  fill <range> <linelength>            fill text", "  s \"old\" \"new\" [<range>]              substitute text", "  abort                                quit without saving", "  save                                 save (effect depends on the type of", "                                       object that is being editted)", "  done                                 done", "----"];
    
    // $#Edited: 13 Dec 95 14:42 Miro ($miro)
    // $#Edited: 03 Jan 96 17:23 Jenner ($jenner)
};

public method .done_cmd() {
    arg [args];
    
    (> .perms(sender()) <);
    if (modified)
        return sender.store_editor();
    sender.quit_editor();
    return "Done. Editor cleared.";
    
    // $#Edited: 09 Dec 95 23:25 Miro ($miro)
    // $#Edited: 03 Jan 96 16:01 Jenner ($jenner)
};

public method .fill_cmd() {
    arg what;
    var start, end, width;
    
    (> .perms(sender()) <);
    what = what.explode();
    catch any
        start = ._parse_range(what[1]);
    with
        return "Illegal range, can't delete.";
    end = start[2];
    start = start[1];
    width = ((what.length()) > 1) ? (what[2]).to_number() : 75;
    text = ((text.subrange(1, start - 1)) + (((text.subrange(start, (end - start) + 1)).join(" ")).wrap_lines(width))) + (text.subrange(end + 1));
    modified = 1;
    line = start;
    return ("Fill completed. Current set to " + tostr(start)) + ".";
    
    // $#Edited: 10 Dec 95 00:47 Miro ($miro)
    // $#Edited: 03 Jan 96 16:01 Jenner ($jenner)
    // $#Edited: 18 Jul 96 14:56 $levi
};

public method .subst_cmd() {
    arg args;
    var start, end, out, i, j, lineno, ind, tmp, rest, opts;
    
    (> .perms(sender()) <);
    if ((!args) || (((args = (args.subrange(2)).explode(args[1], 1)).length()) < 2))
        return ["Usage: s/instr/outstr/range_and_options", "Valid options:", "  g=global", "  r=regexp matching", "  c=case sensitive (regexp only)", "-----"];
    if ((args.length()) == 2)
        args = [@args, ""];
    if (text == [])
        return ["There is no text."];
    if (!(opts = (args[3]).regexp("^([0-9-]*)([a-z]*)$")))
        return ["Invalid range/option spec."];
    catch any
        start = ._parse_range(opts[1]);
    with
        return ["Illegal range, can't list that."];
    end = start[2];
    start = start[1];
    opts = opts[2];
    out = [];
    j = start;
    for i in (text.subrange(start, (end - start) + 1)) {
        if ("r" in opts)
            tmp = i.sed(args[1], args[2], opts);
        else if ("g" in opts)
            tmp = i.replace(args[1], args[2]);
        else if ((ind = (args[1]) in i))
            tmp = ((i.subrange(1, ind - 1)) + (args[2])) + (i.subrange(ind + ((args[1]).length())));
        else
            tmp = i;
        if (tmp != i) {
            text = text.replace(j, tmp);
            lineno = (tostr(j).right(5, (j == line) ? "^" : " ")) + ": ";
            out = [@out, @(lineno + tmp).wrap_lines(sender.linelen(), " ")];
        }
        j++;
    }
    return out ? out : ["No changes."];
    
    // $#Edited: 24 Aug 96 17:12 $jenner
};

public method .enter_cmd() {
    arg what;
    var newtext;
    
    (> .perms(sender()) <);
    if ((> (newtext = sender().read()) <)) {
        modified = 1;
        text = [@text.subrange(1, line - 1), @newtext, @text.subrange(line)];
        return ("Done. " + (newtext.length())) + " line(s) added.";
    } else {
        return "Text unchanged.";
    }
};

public method .mcp_cleanup_session() {
    (> .perms(sender()) <);
    if (type(text) == 'symbol)
        return 1;
    return 0;
    
    // $#Edited: 06 Aug 96 16:52 $jenner
};

public method .is_resumable() {
    return type(text) != 'symbol;
    
    // $#Edited: 06 Aug 96 16:38 $jenner
};

public method .mcp_upload() {
    arg newtext;
    var err;
    
    (> .perms(sender()) <);
    if ((> (err = sender.do_save(finisher_object, finisher, newtext, client_data)) <) == 'clear) {
        sender.quit_editor();
        return "Done. Editor cleared.";
    }
    if (type(err) != 'list)
        modified = 0;
    return err ? err : "Save completed.";
    
    // $#Edited: 18 Aug 96 19:07 $jenner
};

public method .startup_sender(): nooverride {
    return sender;
    
    // $#Edited: 18 Aug 96 19:07 $jenner
};


new object $data_lib: $libraries;

var $root inited = 1;
var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'variables, 'core];
var $root manager = $data_lib;
var $root managed = [$data_lib];
var $root owners = [$data_lib];
var $root owned = [$data_lib];

public method .unparse() {
    arg data, [unparse];
    var str, element, association, pos, method;
    
    return toliteral(data);
    unparse = [@unparse, #[]][1];
    switch (type(data)) {
        case 'integer, 'float:
            return tostr(data);
        case 'string:
            method = (| unparse['string] |) || 'toliteral;
            return $string.(method)(data);
        case 'symbol, 'error, 'buffer:
            return toliteral(data);
        case 'objnum:
            method = (| unparse['objnum] |) || ['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
                return toliteral(data);
    }
    
    // $# Edited 16 Oct 1995 17:58 Lynx ($lynx)
    // $#Edited: 06 Jan 96 13:28 Lynx ($lynx)
};

public method .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;
};

public method .unparse_indent() {
    arg value;
    var s, i, len;
    
    refresh();
    switch (type(value)) {
        case 'list:
            s = map i in [1 .. (len = value.length())] to ((.unparse_indent(value[i])).affix((i < len) ? "," : "")).sum();
            if (((s.mmap('length)).sum()) < 60)
                return [("[" + (s.join())) + "]"];
            else
                return (["[", ""].affix(s.prefix("  "))).affix("]");
        case 'dictionary:
            s = map i in [1 .. (len = (value.keys()).length())] to ((.unparse_indent([(value.keys())[i], value[(value.keys())[i]]])).affix((i < len) ? "," : "")).sum();
            if (((s.mmap('length)).sum()) < 60)
                return [("#[" + (s.join())) + "]"];
            else
                return (["#[", ""].affix(s.prefix("  "))).affix(["]"]);
        case 'frob:
            catch any {
                return [value.unparse()];
            } with {
                catch any
                    return (((["<"].affix(class(value))).affix(",")).affix(.unparse_indent(value.value()))).affix(">");
                with
                    return [.unparse(value)];
            }
        default:
            return [.unparse(value)];
    }
    
    // $#Edited: 13 Aug 96 21:47 $jenner
};

public method .parse_data() {
    arg str;
    var result;
    
    // This method 'parses' a string into a piece of data.
    // First we add a trailing "*" to the string as an eof, and call
    //  ._parse_data()
    result = ._parse_data(str + "*");
    
    // Next, we check the result...
    if (((result[2]).replace(" ", "")) != "*")
        throw(~parse, "Parse error");
    result = result[1];
    return result;
    
    // $#Edited: 16 Aug 96 19:29 $user_charles
    // $#Copied 16 Aug 96 19:52 from $user_charles.parse_data() by $jenner
};

public method ._parse_data() {
    arg str;
    var tmp, i, result, key, assn, type, value;
    
    // Take a string and recursively parse
    switch (str[1]) {
        case "'":
            // Symbol:
            i = 2;
            tmp = "";
    
            // We just scan over the characters, and convert it
            while ((!((str[i]) in ($string.non_alphanumeric()))) || ((str[i]) == "_")) {
                tmp = tmp + (str[i]);
                i = i + 1;
            }
            if (!tmp)
                throw(~parse, "' must precede a symbol");
            result = tosym(tmp);
            str = str.subrange(i);
        case "$":
            // Objref:
            i = 2;
            tmp = "$";
            while ((!((str[i]) in ($string.non_alphanumeric()))) || ((str[i]) == "_")) {
                tmp = tmp + (str[i]);
                i = i + 1;
            }
            result = $object_lib.to_dbref(tmp);
            str = str.subrange(i);
        case "#":
            // Two possibilities here:
            if ((str[2]) == "[") {
                // Dictionary:
                result = #[];
                str = str.subrange(3);
    
                // For each entry, get the key and assn, using ._parse_data()
                while ((str[1]) != "]") {
                    if ((str[1]) != "[")
                        throw(~parse, "Parse error.");
                    str = str.subrange(2);
                    tmp = ._parse_data(str);
                    key = tmp[1];
                    str = tmp[2];
    
                    // Now get the association
                    if ((str[1]) != ",")
                        throw(~parse, "Parse error in dict");
                    str = str.subrange(2);
                    while ((str[1]) == " ")
                        str = str.subrange(2);
                    tmp = ._parse_data(str);
                    assn = tmp[1];
                    str = tmp[2];
                    if ((str[1]) != "]")
                        throw(~parse, "Parse error in dict.");
                    str = str.subrange(2);
                    if ((str[1]) == ",")
                        str = str.subrange(2);
                    while ((str[1]) == " ")
                        str = str.subrange(2);
                    result = result.add(key, assn);
                }
                str = str.subrange(2);
            } else {
                // Another objref:
                i = 2;
                tmp = "#";
                while ((!((str[i]) in ($string.non_alphanumeric()))) || ((str[i]) == "_")) {
                    tmp = tmp + (str[i]);
                    i = i + 1;
                }
                result = $object_lib.to_dbref(tmp);
                str = str.subrange(i);
            }
        case "[":
            // List:
            //  For each element, recursively call ._parse_data()
            result = [];
            str = str.subrange(2);
            while ((str[1]) != "]") {
                tmp = ._parse_data(str);
                result = result + [tmp[1]];
                str = tmp[2];
                if ((str[1]) == ",")
                    str = str.subrange(2);
                while ((str[1]) == " ")
                    str = str.subrange(2);
            }
            str = str.subrange(2);
        case "<":
            // Frob:
            // Get the type:
            str = str.subrange(2);
            tmp = ._parse_data(str);
            type = tmp[1];
            str = tmp[2];
            if ((str[1]) != ",")
                throw(~parse, "Parse error");
            str = str.subrange(2);
            while ((str[1]) == " ")
                str = str.subrange(2);
    
            // Now get the value
            tmp = ._parse_data(str);
            value = tmp[1];
            str = tmp[2];
            if ((str[1]) != ">")
                throw(~parse, "Parse error");
            str = str.subrange(2);
            result = (<type, value>);
        case "`":
            // Buffer:
            str = str.subrange(3);
            tmp = "";
            i = 1;
            while ((str[i]) != "]") {
                tmp = tmp + (str[i]);
                i = i + 1;
            }
            str = str.subrange(i + 1);
            tmp = tmp.replace(" ", "");
            tmp = tmp.explode(",");
            tmp = tmp.mmap('to_number);
            result = `[];
            for i in (tmp)
                result = result + `[i];
        case "~":
            // Error:
            str = str.subrange(2);
            i = 1;
            tmp = "";
            while ((!((str[i]) in ($string.non_alphanumeric()))) || ((str[i]) == "_")) {
                tmp = tmp + (str[i]);
                i = i + 1;
            }
            str = str.subrange(i);
            result = (.eval([("return ~" + tmp) + ";"]))[2];
    
            // result = toerr(tmp);
            // result = ~error;
        case "\"":
            // String:
            i = 2;
            tmp = "";
            while ((str[i]) != "\"") {
                if ((str[i]) == "\\") {
                    if (((str[i + 1]) == "\\") || ((str[i + 1]) == "\"")) {
                        i = i + 2;
                        tmp = tmp + (str[i - 1]);
                    } else {
                        tmp = tmp + (str[i++]);
                    }
                } else {
                    tmp = tmp + (str[i]);
                    i = i + 1;
                }
            }
            result = tmp;
            str = str.subrange(i + 1);
        case "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "-":
            // Integer or float:
            i = 2;
            tmp = str[1];
            while (((str[i]) in ($string.numbers())) || ((str[i]) == ".")) {
                tmp = tmp + (str[i]);
                i = i + 1;
            }
            if ("." in tmp)
                result = tofloat(tmp);
            else
                result = toint(tmp);
            str = str.subrange(i);
        default:
            throw(~parse, "Parse error.");
    }
    return [result, str];
    
    // $#Edited: 16 Aug 96 20:58 $user_charles
    // $#Copied 16 Aug 96 21:01 from $user_charles._parse_data() by $jenner
};


new object $game_lib: $libraries;

var $root manager = $game_lib;
var $root created_on = 809930415;
var $root inited = 1;
var $root flags = ['methods, 'code, 'variables, 'core];
var $root managed = [$game_lib];
var $root owners = [$game_lib];
var $root owned = [$game_lib];

public method .format_rank() {
    arg int1, int2;
    
    if (int1 == int2)
        return tostr(int1).pad(8);
    return ((tostr(int1) + "/") + tostr(int2)).pad(8);
};


new object $symbol: $libraries;

var $root manager = $symbol;
var $root created_on = 811822782;
var $root inited = 1;
var $root flags = ['methods, 'code, 'variables, 'core];
var $root managed = [$symbol];
var $root owners = [$symbol];
var $root owned = [$symbol];

public method .to_string() {
    arg sym;
    
    return tostr(sym);
};


new object $hold_time: $libraries;

var $root manager = $hold_time;
var $root flags = ['methods, 'code, 'variables, 'core];
var $root created_on = 796268969;
var $root owners = [$hold_time];
var $root inited = 1;
var $root managed = [$hold_time];
var $root owned = [$hold_time];

public method .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];
    }
    
    // $#Moved 18 Sep 96 09:46 from $time.month_str() by $admin_brad
};

public method .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);
    
    // $#Moved 18 Sep 96 10:00 from $time.year_str() by $admin_brad
};

public method .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;
            }
    }
    
    // $#Moved 18 Sep 96 10:00 from $time.day_str() by $admin_brad
};

public method .month_day_str() {
    arg [time];
    
    time = .ctime([@time, .time()][1]);
    return (time.subrange(9, 2)).replace(" ", "0");
    
    // $#Moved 18 Sep 96 10:01 from $time.month_day_str() by $admin_brad
};

public method .time_stamp() {
    return ((.ldate('date)) + "/") + ($time.ltime('24hr_sec));
    
    // $#Moved 18 Sep 96 10:02 from $time.time_stamp() by $admin_brad
};


new object $control: $core;

var $root owners = [$control];
var $root created_on = 812170946;
var $root inited = 1;
var $root flags = ['methods, 'code, 'core, 'variables];
var $root owned = [$control];
var $root manager = $reaper;


new object $on_location: $located_location;

var $root manager = $on_location;
var $root created_on = 809991549;
var $root inited = 1;
var $root flags = ['methods, 'code, 'variables, '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 = ['normal, "located_location_3", "a 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;
var $root managed = [$on_location];
var $root owners = [$on_location];
var $root owned = [$on_location];

public method .description() {
    arg flags;
    var line;
    
    line = ((("You see " + ((.contents()).map_to_english('namef))) + " on ") + (.name())) + ".";
    return (> pass(flags) <) + [line];
};


new object $in_location: $located_location;

var $root manager = $in_location;
var $root created_on = 809991552;
var $root inited = 1;
var $root flags = ['methods, 'code, 'variables, '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;
var $root managed = [$in_location];
var $root owners = [$in_location];
var $root owned = [$in_location];

public method .description() {
    arg flags;
    var line;
    
    line = ((("You see " + ((.contents()).map_to_english('namef))) + " in ") + (.name())) + ".";
    return (> pass(flags) <) + [line];
};


new object $lost_and_found: $in_location;

var $root manager = $lost_and_found;
var $root owners = [$lost_and_found];
var $root created_on = 806811775;
var $root inited = 1;
var $root flags = ['methods, 'code, 'variables, 'core];
var $has_commands shortcuts = #[];
var $has_commands remote = #[];
var $has_commands local = #[];
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]]], ["wear", [["wear"], [$wearable_frob]]], ["remove", [["remove"], [$wearable_frob]]]];
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 $described prose = [];
var $location contents = [$robot, $located, $nothing, $wearable_frob];
var $located location = $void;
var $located obvious = 1;
var $thing lock = <$object_lock_frob, [$void]>;
var $root managed = [$lost_and_found];
var $root owned = [$lost_and_found];


new object $trash: $located_location;

var $root manager = $trash;
var $root flags = ['variables, 'methods, 'code, 'core];
var $root created_on = 830126811;
var $root owners = [$trash];
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, "System Trash Can", "the System Trash Can"];
var $named name_aliases = [];
var $gendered gender = $gender_neuter;
var $described prose = <$j_ctext_frob, [["When you discard something, it goes here if you do not manage it."], #[['this, #4]]]>;
var $location contents = [];
var $located location = $void;
var $located obvious = 1;
var $root inited = 1;
var $command_cache remote_cache = #[["@lock", [["@lock"], [$thing]]], ["@unlock", [["@unlock"], [$thing]]], ["@boot", [["@boot"], [$thing]]], ["l", [["l?ook"], [$described]]], ["lo", [["l?ook"], [$described]]], ["loo", [["l?ook"], [$described]]], ["look", [["l?ook"], [$described]]]];
var $trash items = #[[#1004, 834256605]];
var $root managed = [$trash];
var $root owned = [$trash];

public method .did_leave() {
    arg place;
    
    (> pass(place) <);
    (| (items = items.del(sender())) |);
};

public method .did_arrive() {
    arg place;
    var msg;
    
    (> pass(place) <);
    (| (items = items.add(sender(), time())) |);
};


new object $slate: $thing;

var $root inited = 1;
var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'variables, 'core];
var $slate connection = 0;
var $slate remote_host = "";
var $slate remote_port = 0;
var $slate request_method = "";
var $slate received_text = [];
var $located location = $nowhere;
var $located obvious = 1;
var $described prose = [];
var $gendered gender = $gender_neuter;
var $named name = ['uniq, "Generic Slate", "the Generic Slate"];
var $named name_aliases = [];
var $root manager = $slate;
var $root managed = [$slate];
var $root owners = [$slate];
var $root owned = [$slate];

root method .init_slate() {
    connection = 0;
    remote_host = "";
    remote_port = 0;
    request_method = "";
    received_text = [];
};

root method .uninit_slate() {
    connection = 0;
    remote_host = "";
    remote_port = 0;
    request_method = "";
    received_text = [];
};

public method .received_text() {
    (> .perms(sender(), 'this) <);
    return received_text;
};

public method .connection() {
    (> .perms(sender(), 'this) <);
    return connection;
};

public method .remote_host() {
    (> .perms(sender(), 'this) <);
    return remote_host;
};

public method .remote_port() {
    (> .perms(sender(), 'this) <);
    return remote_port;
};

public method .request_method() {
    (> .perms(sender(), 'this) <);
    return request_method;
};

public method .send() {
    arg line;
    
    (> .perms(sender(), 'this) <);
    connection.send(line);
};

public method .connection_ending();


new object $nothing: $thing;

var $root inited = 1;
var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'variables, 'core];
var $located location = $lost_and_found;
var $located obvious = 1;
var $gendered gender = $gender_neuter;
var $described prose = [];
var $named name = ['prop, "nothing whatsoever", "nothing whatsoever"];
var $named name_aliases = [];
var $root manager = $nothing;
var $root managed = [$nothing];
var $root owners = [$nothing];
var $root owned = [$nothing];


new object $antisocial: $user_interfaces, $has_messages;

var $root manager = $antisocial;
var $has_messages messages = #[[$antisocial, #[["test", <$j_ctext_frob, [[<$j_generator, ["actor", [], [], 'gen_actor]>, " tests the $antisocial parent."], #[['this, $antisocial]]]>], ["kick", <$j_ctext_frob, [[<$j_generator, ["actor", [], [], 'gen_actor]>, " kicks ", <$j_generator, ["who", [], [], 'gen_who]>, " rather firmly in the kiester."], #[['this, $antisocial]]]>], ["eye", <$j_ctext_frob, [[<$j_generator, ["actor", [], [], 'gen_actor]>, " eyes ", <$j_generator, ["who", [], [], 'gen_who]>, " warily."], #[['this, $antisocial]]]>], ["ice", <$j_ctext_frob, [[<$j_generator, ["actor", [], [], 'gen_actor]>, " pours a large bucket of ice water over ", <$j_generator, ["who", [], [], 'gen_who]>, "."], #[['this, $antisocial]]]>], ["fart", <$j_ctext_frob, [[<$j_generator, ["actor", [], [], 'gen_actor]>, " farts in ", <$j_generator, ["who", [], [], 'gen_who]>, "'s general direction."], #[['this, $antisocial]]]>], ["pat", <$j_ctext_frob, [[<$j_generator, ["actor", [], [], 'gen_actor]>, " pats ", <$j_generator, ["who", [], [], 'gen_who]>, " onna head."], #[['this, $antisocial]]]>], ["pummel", <$j_ctext_frob, [[<$j_generator, ["actor", [], [], 'gen_actor]>, " pummels ", <$j_generator, ["who", [], [], 'gen_who]>, " into a milky pulp."], #[['this, $antisocial]]]>], ["poke", <$j_ctext_frob, [[<$j_generator, ["actor", [], [], 'gen_actor]>, " pokes ", <$j_generator, ["who", [], [], 'gen_who]>, ". ", <$j_generator, ["who", [], [], 'gen_who]>, " emits a sound similiar to that of the pilsbury doughboy."], #[['this, $antisocial]]]>], ["prod", <$j_ctext_frob, [[<$j_generator, ["actor", [], [], 'gen_actor]>, " prods ", <$j_generator, ["who", [], [], 'gen_who]>, " with a rather large high voltage cattle prod."], #[['this, $antisocial]]]>], ["test.actor", <$j_ctext_frob, [["You test the $antisocial parent."], #[['this, $antisocial]]]>], ["kick.actor", <$j_ctext_frob, [["You kick ", <$j_generator, ["who", [], [], 'gen_who]>, " rather firmly in the kiester."], #[['this, $antisocial]]]>], ["kick.who", <$j_ctext_frob, [[<$j_generator, ["actor", [], [], 'gen_actor]>, " kicks you rather firmly in the kiester."], #[['this, $antisocial]]]>], ["eye.actor", <$j_ctext_frob, [["You eye ", <$j_generator, ["who", [], [], 'gen_who]>, " warily."], #[['this, $antisocial]]]>], ["eye.who", <$j_ctext_frob, [[<$j_generator, ["actor", [], [], 'gen_actor]>, " eyes you warily."], #[['this, $antisocial]]]>]]]];
var $root flags = ['variables, 'methods, 'code, 'command, 'command_cache, 'fertile, 'core];
var $root created_on = 838182893;
var $root owners = [$antisocial];
var $antisocial antisocial_msgs = #[["feh", <$j_ctext_frob, [[<$j_generator, ["actor", [], [], 'gen_actor]>, " kicks ", <$j_generator, ["who", [], [], 'gen_who]>, " rather firmly in the kiester."], #[['this, $antisocial]]]>]];
var $has_commands shortcuts = #[];
var $has_commands remote = #[];
var $has_commands local = #[["test", [["test", "", "test", 'anti_cmd, #[]]]], ["ice", [["ice", "*", "ice <object>", 'anti_cmd, #[[1, ['object, []]]]]]], ["eye", [["eye", "*", "eye <object>", 'anti_cmd, #[[1, ['object, []]]]]]], ["fart", [["fart", "*", "fart <object>", 'anti_cmd, #[[1, ['object, []]]]]]], ["kick", [["kick", "*", "kick <object>", 'anti_cmd, #[[1, ['object, []]]]]]], ["pat", [["pat", "*", "pat <object>", 'anti_cmd, #[[1, ['object, []]]]]]], ["pummel", [["pummel", "*", "pummel <object>", 'anti_cmd, #[[1, ['object, []]]]]]], ["poke", [["poke", "*", "poke <object>", 'anti_cmd, #[[1, ['object, []]]]]]], ["prod", [["prod", "*", "prod <object>", 'anti_cmd, #[[1, ['object, []]]]]]]];
var $root inited = 1;
var $has_messages message_info = #[["test", #[]], ["kick", #[]], ["eye", #[]], ["ice", #[]], ["fart", #[]], ["pat", #[]], ["pummel", #[]], ["poke", #[]], ["prod", #[]]];
var $root managed = [$antisocial];
var $root owned = [$antisocial];

public method .set_antisocial() {
    arg name, message;
    var mes, partial, compiler;
    
    (> .perms(sender(), 'writer) <);
    if (!antisocial_msgs)
        antisocial_msgs = #[];
    compiler = $j_compiler;
    message = compiler.compile_cml(message);
    antisocial_msgs = antisocial_msgs.add(name, message);
    
    // $#Edited: 23 Jul 96 22:51 $xymox
};

public method .antisocial_msgs() {
    return antisocial_msgs;
    
    // $#Edited: 23 Jul 96 23:03 $xymox
};

public method .test() {
    arg [who];
    var vars, m, message;
    
    if (who)
        who = .match_environment(who[1]);
    vars = #[["$actor", this()], ["actor", .name()], ["$who", who], ["who", who.name()]];
    message = (definer().antisocial_msgs())["feh"];
    m = message.set_vars(vars);
    .tell(m);
    .announce(m, this());
    
    // $#Edited: 23 Jul 96 23:23 $xymox
};

protected method .anti_cmd() {
    arg cmdstr, cmd, [who];
    var message, vars, victim, m;
    
    vars = #[["$actor", this()], ["actor", .name()]];
    if (who)
        vars = (vars.add("$who", who[1])).add("who", (who[1]).name());
    .announce(.eval_message(cmd, vars));
};


new object $social: $has_messages, $user_interfaces;

var $root manager = $social;
var $has_messages message_info = #[["bow", #[]]];
var $root flags = ['variables, 'methods, 'code, 'command_cache, 'core];
var $root created_on = 838260513;
var $root owners = [$social];
var $has_messages messages = #[[$social, #[["bow.actor", <$j_ctext_frob, [["You bow to ", <$j_generator, ["victim", [], [], 'gen_victim]>, "."], #[['this, $social]]]>], ["bow.victim", <$j_ctext_frob, [[<$j_generator, ["actor", [], [], 'gen_actor]>, " bows to you."], #[['this, $social]]]>], ["bow", <$j_ctext_frob, [[<$j_generator, ["actor", [], [], 'gen_actor]>, " bows to ", <$j_generator, ["victim", [], [], 'gen_victim]>, "."], #[['this, $social]]]>]]]];
var $has_commands shortcuts = #[];
var $has_commands remote = #[];
var $has_commands local = #[["bow", [["bow", "*", "bow <object>", 'social_cmd, #[[1, ['object, []]]]]]]];
var $root inited = 1;
var $root managed = [$social];
var $root owned = [$social];

public method .social_cmd() {
    arg cmdstr, cmd, [who];
    var vars, i, n;
    
    vars = #[["$actor", this()], ["actor", .name()]];
    n = 0;
    for i in (who) {
        if ((| i.name() |)) {
            vars = (vars.add(n ? "$victim_" + n : "$victim", i)).add(n ? "victim_" + n : "victim", i.name());
            n++;
        }
    }
    .announce(.eval_message(cmd, vars));
    
    // $#Edited: 24 Jul 96 20:33 $jenner
    // $#Edited: 26 Jul 96 16:03 $jenner
};


new object $climate: $has_commands;

var $root manager = $climate;
var $root child_index = 1;
var $root flags = ['variables, 'methods, 'code, 'core];
var $root created_on = 839895133;
var $root owners = [$climate];
var $climate data = 0;
var $has_commands shortcuts = #[];
var $has_commands remote = #[["@setup", [["@setup", "*", "@setup <this>", 'setup_cmd, #[[1, ['this, []]]]]]], ["@list-climate", [["@list-climate", "*", "@list-climate <this>", 'show_cmd, #[[1, ['this, []]]]]]], ["@add-weather", [["@add-weather", "* to *", "@add-weather <any> to <this>", 'add_weather_cmd, #[[1, ['any, []]], [3, ['this, []]]]]]], ["@del-weather", [["@del-weather", "* from *", "@del-weather <any> from <this>", 'del_weather_cmd, #[[1, ['any, []]], [3, ['this, []]]]]]]];
var $has_commands local = #[];
var $root inited = 1;
var $root managed = [$climate];
var $root owned = [$climate];

public method .setup_cmd() {
    arg cmdstr, cmd, this;
    var s;
    
    s = sender();
    (> .perms(s) <);
    s.tell("WARNING: This will erase all the data on this object. You can @abort if you want to avoid it.");
    data = $climate_frob.read_new(s);
    s.tell("Setup done.");
    
    // $#Edited: 12 Aug 96 18:44 $jenner
};

public method .add_weather_cmd() {
    arg cmdstr, cmd, weather, from, this;
    var s;
    
    s = sender();
    (> .perms(s) <);
    data = data.read_weather(s, tosym(weather.strip()));
    
    // $#Edited: 12 Aug 96 18:44 $jenner
};

public method .del_weather_cmd() {
    arg cmdstr, cmd, weather, from, this;
    var s;
    
    s = sender();
    (> .perms(s) <);
    data = data.del_weather(tosym(weather.strip()));
    
    // $#Edited: 12 Aug 96 18:44 $jenner
};

public method .show_cmd() {
    arg cmdstr, cmd, this;
    
    return data.show();
    
    // $#Edited: 12 Aug 96 18:44 $jenner
};


new object $climate_taobh_thiar: $climate;

var $root manager = $climate_taobh_thiar;
var $root flags = ['variables, 'methods, 'code, 'core];
var $root created_on = 839896989;
var $root owners = [$climate_taobh_thiar];
var $climate data = <$climate_frob, [['visibility, 'severity], ['winter, 'spring, 'summer, 'fall], #[['sunny, [[2, 4], [20, 30, 40, 50], <$j_ctext_frob, [["Sunny, with occasional clouds. Nothing to worry about."], #[['this, $climate_frob]]]>, []]]]]>;
var $has_commands shortcuts = #[];
var $has_commands remote = #[];
var $has_commands local = #[];
var $root inited = 1;
var $root managed = [$climate_taobh_thiar];
var $root owned = [$climate_taobh_thiar];


new object $has_reactions: $foundation;

var $root manager = $has_reactions;
var $root created_on = 809243338;
var $root inited = 1;
var $root flags = ['methods, 'code, 'variables, 'core];
var $root managed = [$has_reactions];
var $root owners = [$has_reactions];
var $root owned = [$has_reactions];