/
CDC-1.2b/
CDC-1.2b/src/
object $root

var $root manager $root
var $root owners [$core]
var $root writable [$root]
var $root readable ['parameters, 'methods, 'code]
var $root trusted []
var $root owned []
var $root child_index 0
var $root fertile 0
var $root inited 0
var $root info ["The root object is the ultimate parent of all objects. It defines beahvior of all objects. It controls initialization, deinitialization, creation, destruction, and identification of objects, as well as permission checking, and ownership."]
var $root quota 75000
var $root dbref 'root

method init_root
    (> .perms(caller(), $root) <);
    child_index = 0;
    fertile = 0;
    
    // for now set all the ownership stuff as this(), it can be reset later.
    manager = this();
    owned = [this()];
    owners = [this()];
    writable = [];
    readable = ['parameters, 'methods, 'code];
.

method uninit_root
    var obj, x, objs;
    
    (> .perms(caller(), $root) <);
    objs = [];
    catch any {
        for obj in (owned) {
            if (obj == this())
                continue;
            catch any {
                if ((obj.manager()) == this()) {
                    for x in (obj.owners()) {
                        if (valid(x) && (x != this()))
                            objs = [@objs, x];
                    }
                    obj.chown([$reaper, @objs]);
                    obj.set_manager($reaper);
                }
            } with handler {
                // this is getting redundant, but there is a reason for it.
                (| obj.chown([$reaper]) |);
                (| obj.del_owner(this()) |);
                $reaper_log.log(("$root.uninit_root(): Unable to give " + (obj.dbref())) + " an owner.");
            }
        }
        for obj in (owners)
            (| obj.del_owned_obj(this()) |);
    } with handler {
        $brandon.tell("Traceback from $root.uninit_root()");
        $brandon.tell_traceback(traceback());
    }
.

method initialize
    disallow_overrides;
    var ancestors, ancestor, method_name, pos;
    
    if ((caller() != $sys) && (sender() != this()))
        throw(~perm, "Caller is not $sys and sender is not this.");
    if (inited)
        throw(~perm, "Already initialized.");
    ancestors = ancestors();
    for pos in [0 .. listlen(ancestors) - 1] {
        ancestor = ancestors[listlen(ancestors) - pos];
        method_name = tosym("init_" + tostr(ancestor.dbref('symbol)));
        catch ~methodnf {
            if (find_method(method_name) != ancestor)
                throw(~perm, ((("Initialization method for " + (ancestor.namef('ref))) + " in wrong place. Found on ") + (find_method(method_name).namef('ref))) + ".");
            .(method_name)();
        }
    }
    inited = 1;
.

method uninitialize
    disallow_overrides;
    var ancestor, owner, obj;
    
    .perms(caller(), $root, $sys);
    for ancestor in (ancestors())
        (| .(tosym("uninit_" + tostr(ancestor.dbref('symbol))))() |);
.

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

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

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);
.

method will_inherit
    arg obj;
    
    // Throw an error if it's not okay for obj to inherit from us.
    if ((!fertile) && ((!(.trusts(obj))) && (!(obj.has_ancestor(this())))))
        throw(~perm, "Refuse to be parent of prospective child.");
.

method manager
    disallow_overrides;
    
    return manager;
.

method owners
    disallow_overrides;
    
    return owners;
.

method writers
    disallow_overrides;
    
    return writable + [manager, this()];
.

method trusted
    return (trusted ? trusted | []) + [this()];
.

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

method is_readable_by
    disallow_overrides;
    arg obj;
    
    // will eventually get this working with lock frobs, for now
    // ,readable is a simple list of what everyone can read:
    // 'methods, 'parameters, 'code
    if (.trusts(obj))
        return ['methods, 'parameters, 'code];
    else
        return readable;
.

method trusts
    arg obj;
    
    return (| obj in (.trusted()) |) || ((.is_writable_by(obj)) || ($sys.is_system(obj)));
.

method fertile
    disallow_overrides;
    
    return fertile;
.

method set_fertile
    arg val;
    
    (> .perms(sender(), 'manager) <);
    fertile = val ? 1 | 0;
.

method set_manager
    disallow_overrides;
    arg obj;
    
    (> .perms(sender(), 'manager) <);
    if (type(obj) != 'dbref)
        throw(~type, "Managers must be given as a single dbref.");
    manager = obj;
.

method set_writable
    disallow_overrides;
    arg what;
    
    .perms(sender(), 'manager);
    writable = what;
.

method add_writer
    disallow_overrides;
    arg what;
    
    (> .perms(sender(), 'manager) <);
    writable = [@writable, what];
.

method del_writer
    disallow_overrides;
    arg what;
    
    (> .perms(sender(), 'manager) <);
    writable = setremove(writable, what);
.

method set_readable
    disallow_overrides;
    arg what;
    
    (> .perms(sender(), 'manager) <);
    readable = what;
.

method set_trusted
    disallow_overrides;
    arg what;
    
    (> .perms(sender(), 'manager) <);
    trusted = what;
.

method add_trusted
    disallow_overrides;
    arg what;
    
    (> .perms(sender(), 'manager) <);
    trusted = [@trusted, what];
.

method del_trusted
    disallow_overrides;
    arg what;
    
    (> .perms(sender(), 'manager) <);
    trusted = setremove(trusted, what);
.

method as_this_run
    arg obj, method, [args];
    
    // run with this()'s perms -- unless they have changed .trusts,
    // it should just check against writers.
    if (!(.trusts(sender())))
        throw(~perm, "Sender not allowed to gain access to object perms.");
    return (> obj.(method)(@args) <);
.

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

method del_parent
    arg parent;
    var parents;
    
    (> .perms(sender(), 'manager) <);
    if (!valid(parent))
        throw(~type, "Not a valid parent, must send a dbref or pointer");
    parents = .parents();
    if (!(parent in parents))
        throw(~parentnf, "No such parent as " + ($data.unparse(parent)));
    parents = setremove(parents, parent);
    (> .chparents(parents) <);
.

method spawn
    arg [suffix];
    var obj, tmp, dbref, owner, mngr, na;
    
    // will later adjust this for .is_readable_by()
    // use this rather than $string.non_alphanumeric because it doesn't
    // have '_'
    na = "!@#$%^&*()+-=~`'{}[]|/?\"\\,.<>;: ";
    if ((!fertile) && (((.is_readable_by(sender())) != ['methods, 'parameters, 'code]) && (!($sys.is_system(sender())))))
        throw(~perm, "Not fertile or readable.");
    owner = sender();
    
    // Figure out the suffix from the arguments and child index.
    if (!(owner.quota_valid()))
        throw(~quota, "Sender does not have the available quota");
    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);
    } else {
        // make sure it doesn't exist already:
        dbref = tostr(.dbref('symbol));
        tmp = .dbref('symbol);
        while ((| get_objnum(tmp) |)) {
            child_index = child_index + 1;
            tmp = tosym((dbref + "_") + tostr(child_index));
        }
        suffix = tostr(child_index);
    }
    
    // Ask the system object for a child.
    obj = $sys.spawn_sender(suffix, owner);
    return obj;
.

method destroy
    disallow_overrides;
    
    // This doesn't actually destroy us immediately, but we will go away when
    // nothing is holding onto us any more.
    (> .perms(sender(), 'manager) <);
    (| .uninitialize() |);
    $sys.destroy_sender();
.

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

method parameters
    (> .perms(sender(), 'this) <);
    return parameters();
.

method del_parameter
    arg name;
    
    (> .perms(sender()) <);
    (> del_parameter(name) <);
.

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

method methods
    disallow_overrides;
    
    if (!('methods in (.is_readable_by(sender()))))
        throw(~perm, ((sender().dbref()) + " doesn't have permission to find methods on ") + (.dbref()));
    return methods();
.

method parents
    disallow_overrides;
    
    return parents();
.

method children
    disallow_overrides;
    
    return children();
.

method ancestors
    disallow_overrides;
    
    return ancestors();
.

method find_method
    disallow_overrides;
    arg name;
    
    if (!('methods in (.is_readable_by(sender()))))
        throw(~perm, ((sender().dbref()) + " doesn't have permission to find methods on ") + (.dbref()));
    return (> find_method(name) <);
.

method find_next_method
    disallow_overrides;
    arg name, after;
    
    if (!('methods in (.is_readable_by(sender()))))
        throw(~perm, ((sender().dbref()) + " doesn't have permission to find methods on ") + (.dbref()));
    return (> find_next_method(name, after) <);
.

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

method compile
    arg code, name;
    
    // allow overrides -- may be a problem...
    (> .perms(sender()) <);
    return compile(code, name);
.

method show
    disallow_overrides;
    var output, actor, data, parent, readable, m, p, x, line;
    
    actor = sender();
    readable = .is_readable_by(actor);
    data = $sys.sender_data();
    
    // the beginning.
    output = ((("Object: " + (.namef('ref))) + " [") + tostr(.size())) + " bytes]";
    output = [output, "Parents: " + ($data.unparse(parents()))];
    
    // Methods
    if ('methods in readable) {
        output = output + ["Methods:"];
        for m in (.method_info()) {
            output = output + [((("  " + tostr(m[2])) + "(") + (m[3])) + ")"];
            pause();
        }
    } else {
        output = output + ["  ** No permission to list Methods **"];
    }
    
    // Parameters
    if ('parameters in readable) {
        for parent in (data) {
            if (valid(parent[1])) {
                output = output + [((parent[1]).namef('xref)) + " Parameters:"];
                if ('parameters in ((parent[1]).is_readable_by(actor))) {
                    for p in (parent[2]) {
                        line = (("  " + tostr(p[1])) + ": ") + ($data.unparse(p[2]));
                        output = output + [line];
                    }
                } else {
                    output = output + ["  ** Permission Denied **"];
                }
            } else {
                output = output + [toliteral(parent[1]) + " Parameters:"];
                for p in (parent[2]) {
                    line = (("  " + tostr(p[1])) + ": ") + ($data.unparse(p[2]));
                    output = output + [line];
                }
            }
            pause();
        }
    } else {
        output = output + ["  ** No permission to list Parameters **"];
    }
    
    // Return what we've got.
    return output;
.

method has_ancestor
    disallow_overrides;
    arg obj;
    
    return has_ancestor(obj);
.

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

method method_info
    arg [args];
    var filter, maxp, anc, ancs, method, methods_found;
    
    // lists methods on objects which are ancestors of this and parents of args[1]
    // method info items are of the form:
    // [$definer, 'method, "args", "flags", "first comment/return value"]
    if (!('methods in (.is_readable_by(sender()))))
        throw(~perm, (("Method code on " + (.namef('ref))) + " is not readable by ") + (sender().namef('ref)));
    if (args) {
        maxp = args[1];
        args = delete(args, 1);
    }
    if (listlen(args) >= 2) {
        filter = sublist(args, 1, 2);
        args = delete(delete(args, 1), 1);
    } else {
        filter = [$misc, '_null_method_filter];
    }
    methods_found = [];
    ancs = .ancestors();
    if (!(maxp in ancs)) {
        maxp = this();
        ancs = [this()];
    }
    for anc in (ancs) {
        if (anc.has_ancestor(maxp)) {
            for method in (anc.methods()) {
                if ((filter[1]).(filter[2])(anc, method, @args))
                    methods_found = [@methods_found, ._get_method_info(anc, method)];
            }
        }
    }
    return methods_found;
.

method parameter_info
    arg [args];
    var data, pattern, max_parent, pparams, param;
    
    // data() reformated as
    // [[$parent,'param,val],...]
    if (!('parameters in (.is_readable_by(sender()))))
        throw(~perm, (("Parameters on " + (.dbref())) + " are not readable by ") + (sender().dbref()));
    max_parent = [@args, this()][1];
    data = [];
    for pparams in ($list.reverse($sys.sender_data())) {
        if (valid(pparams[1]) && ((pparams[1]).has_ancestor(max_parent))) {
            for param in (pparams[2])
                data = [@data, [pparams[1], @param]];
        }
    }
    return data;
.

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

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

method descendants
    disallow_overrides;
    var kid, kids;
    
    // .descendants()
    // -> Recursive list of all children of this object -- try not to use
    kids = children();
    for kid in (kids)
        kids = union(kids, kid.descendants());
    return kids;
.

method get_quota
    disallow_overrides;
    
    return quota;
.

method owned
    disallow_overrides;
    
    return owned;
.

method quota_valid
    disallow_overrides;
    
    // if (!.has_ancestor($user) && !$sys.is_admin(this()))
    //  return 0;
    // will put quota checking in here
    return 1;
.

method del_owned_obj
    disallow_overrides;
    arg obj;
    
    (> .perms(caller(), $root) <);
    owned = setremove(owned, obj);
.

method add_owned_obj
    disallow_overrides;
    arg obj;
    
    (> .perms(caller(), $root, $sys) <);
    if (obj in owned)
        throw(~addowned, ((.namef('ref)) + " already owns ") + (obj.namef('ref)));
    owned = [@owned, obj];
.

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

method set_info
    arg text;
    
    (> .perms(sender(), 'manager) <);
    info = text;
.

method info
    return info;
.

method _display_descendants
    arg space, checked, base;
    var space, checked, c, anc, biguglylist, id, perms;
    
    perms = "";
    if (fertile)
        perms = "F";
    for c in (.readable())
        perms = perms + (tostr(c)[1]);
    id = (space + (.namef('xref))) + (perms ? (" [" + perms) + "]" | perms);
    for anc in (checked) {
        if (.has_ancestor(anc))
            return [id + "  (above)"];
    }
    if (listlen(.parents()) > 1)
        id = id + " (MI)";
    else
        id = id + "";
    biguglylist = [id];
    space = space + "   ";
    
    // check children
    if ((!base) || (base != this())) {
        for c in (.children()) {
            biguglylist = biguglylist + (c._display_descendants(space, checked, base));
            checked = setadd(checked, c);
            pause();
        }
    }
    return biguglylist;
.

method _display_ancestors
    arg [args];
    var space, checked, c, anc, biguglylist, id, perms;
    
    space = args ? args[1] | "";
    checked = (listlen(args) > 1) ? args[2] | [];
    perms = "";
    if (fertile)
        perms = "F";
    for c in (.readable())
        perms = perms + (tostr(c)[1]);
    id = (space + (.namef('ref))) + (perms ? (" [" + perms) + "]" | perms);
    for anc in (checked) {
        if (.has_ancestor(anc))
            return [id + "  (above)"];
    }
    if (listlen(.parents()) > 1)
        id = id + " (MI)";
    else
        id = id + "";
    biguglylist = [id];
    space = space + "  ";
    
    // check children
    for c in (.parents()) {
        biguglylist = biguglylist + (c._display_ancestors(space, checked));
        checked = setadd(checked, c);
        pause();
    }
    return biguglylist;
.

method _get_method_info
    disallow_overrides;
    arg anc, method;
    var code, lines, dis_flag, meth_args, first_comment;
    
    code = anc.list_method(method);
    lines = listlen(code);
    if (lines > 5)
        code = sublist(code, 1, 5);
    dis_flag = 0;
    if (code) {
        if ("disallow_overrides;" == (code[1])) {
            dis_flag = 1;
            code = delete(code, 1);
        }
        if (code && ((code[1]) && (((code[1])[1]) == "a"))) {
            meth_args = match_pattern("arg *;", code[1])[1];
            code = delete(code, 1);
        } else {
            meth_args = "";
        }
        if (code && ((!(code[1])) || (((code[1])[1]) == "v")))
            code = delete(code, 1);
        if (code && ((!(code[1])) || (((code[1])[1]) == "v")))
            code = delete(code, 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, dis_flag, lines, first_comment];
.

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

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;
.

method readable
    return readable;
.

method chown
    disallow_overrides;
    arg new_owners;
    var old_owners, obj, sofar, this;
    
    // must be called to add/del/change any ownership.
    (> .perms(sender(), .manager(), @$sys.system()) <);
    
    // make sure the new owners list is legit.
    if (type(new_owners) != 'list)
        throw(~type, "New owners must be given as a list of objects");
    if (!($sys.is_system(sender()))) {
        for obj in (new_owners) {
            if (!(obj.has_ancestor($user)))
                throw(~perm, (obj.namef('ref)) + " is not a user object.");
        }
    }
    
    // few basic (re)settings
    old_owners = $list.flatten(owners);
    new_owners = $list.flatten(new_owners);
    owners = [];
    this = this();
    sofar = [];
    
    // first make it so nobody owns it.
    for obj in (old_owners)
        (| obj.del_owned_obj(this) |);
    
    // set all the new owners
    for obj in (new_owners) {
        catch any {
            obj.add_owned_obj(this);
        } with handler {
            if (error() == ~addowned)
                continue;
    
            // didn't work out, set it back the way it was.
            for obj in (sofar)
                (| obj.del_owned_obj(this) |);
            for obj in (old_owners) {
                if ((| obj.add_owned_obj(this) |))
                    owners = [@owners, obj];
            }
            throw(~chown, "Unable to add %O as an owner, chown unsuccessful.", obj);
        }
        sofar = [@sofar, obj];
    }
    owners = new_owners;
.

method debug
    arg [stuff];
    var x, line, mngr;
    
    line = (sender().dbref()) + " debug:";
    for x in (stuff)
        line = (line + " ") + ((type(x) == 'string) ? x | ($data.unparse(x)));
    (| (.manager()).tell(line) |);
.

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

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

method set_dbref
    arg new_dbref;
    var old_dbref;
    
    // Only accept calls from owners or admins.
    if (!(.is_writable_by(sender())))
        throw(~perm, "Sender not an owner.");
    
    // Make sure first argument is a symbol.
    if (type(new_dbref) != 'symbol)
        throw(~type, "New dbref is not a symbol.");
    
    // Make sure everything is lowercase.
    new_dbref = tosym(lowercase(tostr(new_dbref)));
    
    // Do nothing if new_dbref isn't different.
    if (new_dbref == dbref)
        return;
    
    // Grab the new dbref
    (> $sys.assign_dbref(new_dbref) <);
    old_dbref = dbref;
    dbref = new_dbref;
    
    // If we already had a dbref, get rid of the old one.
    if (old_dbref)
        (> $sys.deassign_dbref(old_dbref) <);
.

method name
    arg [args];
    
    return .dbref();
.

method del_owner
    disallow_overrides;
    arg owner;
    
    (> .perms(caller(), $root) <);
    if (listlen(owners) != 1)
        owners = setremove(owners, owner);
    else
        .chown([$no_one]);
.

method add_owner
    disallow_overrides;
    arg owner;
    
    (> .perms(caller(), $root) <);
    owners = [@owners, owner];
.

method generate_html
    arg [args];
    var actor, readable, name, out, line, obj, col, colx, objs, x;
    
    actor = sender();
    readable = .is_readable_by(actor);
    name = .namef('ref);
    out = ["<head>", ("<title>" + name) + "</title>", "<center>"];
    out = [@out, ("<h1>" + name) + "</h1>", "</center>"];
    out = [@out, "<hr width=50% align=middle>", "<body>"];
    line = ("<a href=\"/bin/show?" + tostr(.dbref('symbol))) + "\">";
    out = [@out, ("<center>" + line) + "show this object.</a></center>"];
    line = [];
    for obj in (.parents())
        line = [@line, $http.make_href_string(obj)];
    out = [@out, "<P>Parent(s): " + ($list.to_english(line)), "<pre>"];
    
    // duplicate the $code.generate_family_listing code because we
    // want to put in hrefs.
    objs = .children();
    if (objs) {
        out = [@out, "Children:"];
        col = ((| sender().linelen() |) || 79) / 8;
        colx = col * 3;
        line = pad("Name", colx + 2) + pad(" Perms", col - 2);
        out = [@out, (line + pad("Size ", -col)) + "Manager"];
        for obj in (objs) {
            name = obj.namef('xref);
            line = $http.make_href_string(obj, pad(name, colx + 2));
            line = line + pad($object.see_perms(obj), col - 2);
            line = line + pad(tostr(obj.size()) + " ", -col);
            x = obj.manager();
            if (!valid(x))
                name = pad(("** invalid object *(" + tostr(x)) + ") **", colx);
            else
                name = $http.make_href_string(x, pad(x.namef('xref), colx));
            line = line + name;
            out = [@out, line];
        }
    }
    out = [@out, "</pre>"];
    if (info) {
        if (type(info) == 'frob)
            out = [@out, info.translate_to("text/html")];
        else
            out = [@out, "<hr width=50% align=middle>", @info];
    }
    return out;
.

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

method quota
    return quota;
.

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

method __docore
    owned = [this()];
.