object $root;
var $root child_index = 0;
var $root created_on = 0;
var $root defined_settings = #[["help-node", #[['get, ['help_node]], ['set, ['set_help_node_setting]], ['parse, ['parse_help_node_setting]], ['format, ['format_help_node_setting]]]]];
var $root fertile = 1;
var $root flags = ['methods, 'code, 'core, 'variables];
var $root help_node = 0;
var $root inited = 0;
var $root managed = 0;
var $root manager = $root;
var $root quota = 75000;
var $root quota_exempt = 0;
var $root settings = 0;
var $root trusted = [];
var $root trusted_by = 0;
var $root writers = [$root];
var $root writes = 0;
protected method ._all_defined_settings(): nooverride {
var a, all, d;
all = #[];
for a in (ancestors()) {
catch any {
for d in (dict_keys(a.defined_settings()))
all = dict_add(all, d, a);
}
}
return all;
};
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 ._clear_setting(): nooverride {
arg name;
if (settings && dict_contains(settings, name)) {
settings = dict_del(settings, name);
if (!settings)
clear_var('settings);
}
};
public method ._display_ancestors() {
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 parents
if ((!maxlev) || (maxlev != levels)) {
for c in (.parents()) {
list += c._display_ancestors(space, checked, levels, maxlev);
checked = dict_add(checked, c, 1);
pause();
}
}
return list;
};
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 = regexp(code[1], "arg ([^;]);");
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 .add_flag(): nooverride {
arg flag;
(> .perms(sender(), 'manager) <);
(> $sys.touch('coreok) <);
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);
};
root method .add_managed_obj(): nooverride {
managed = (managed || []).setadd(sender());
};
public method .add_method() {
arg code, name, @evalonly;
var l, m, line, errs;
(> .perms(sender()) <);
if (evalonly && (caller() == $programmer))
(> $sys.touch('lockok) <);
else
(> $sys.touch() <);
// check for a few things only admins can do
if (!(sender().is($admin))) {
for l in [1 .. listlen(code)] {
line = code[l];
if ((m = line.match_regexp("[^a-z0-9_.]anticipate_assignment\("))) {
(> sender().tell($code_lib.point_to_line("ERROR: call to anticipate_assignment()", ((m[1])[1]) + 2, ((m[1])[2]) - 2, l, line)) <);
throw(~perm, "anticipate_assignment() may only be used by an administrator.");
}
}
}
errs = (> add_method(code, name) <);
for l in [1 .. listlen(errs)] {
line = errs[l];
if (match_regexp(line, "Line [0-9]+: Unknown function length.")) {
line = substr(line, 1, strlen(line) - 1);
errs = replace(errs, l, line + "(), try listlen(), strlen() or buflen()");
}
}
return errs;
};
public method .add_parent() {
arg parent;
(> .perms(sender(), 'manager) <);
(> $sys.touch() <);
(> parent.will_inherit(sender()) <);
if (.has_ancestor(parent))
throw(~perm, ((this() + " already has ") + parent) + " as an ancestor.");
.change_parents(parents() + [parent]);
};
public method .add_trusted(): nooverride {
arg obj;
(caller() == definer()) || (> .perms(sender(), 'manager) <);
(> $sys.touch('coreok) <);
trusted = (trusted || []).setadd(obj);
obj.add_trusted_obj();
};
root method .add_trusted_obj(): nooverride {
trusted_by = (trusted_by || []).setadd(sender());
};
public method .add_var(): nooverride {
arg name, @args;
var tmp, kid;
(> .perms(sender()) <);
(> $sys.touch() <);
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());
}
}
};
root method .add_writable_obj(): nooverride {
writes = (writes || []).setadd(sender());
};
public method .add_writer(): nooverride {
arg obj;
(> .perms(sender(), 'manager) <);
(> $sys.touch('coreok) <);
writers = (writers || []).setadd(obj);
obj.add_writable_obj();
};
public method .all_defined_settings(): nooverride {
if (!(.has_flag('variables, sender())))
throw(~perm, ((sender().namef('ref)) + " does not have permission to view settings on ") + (.name('ref)));
return ._all_defined_settings();
};
public method .anc2(): nooverride {
var i, c, parent, d;
d = #[];
for parent in (parents())
d = dict_add(d, parent, 1);
while ((| (c = dict_keys(d)[++i]) |)) {
for parent in (c.parents()) {
pause();
d = dict_add(d, parent, 1);
}
pause();
}
return dict_keys(d);
};
public method .ancestors(): nooverride {
arg @args;
return ancestors(@args);
};
public method .ancestors_descending() {
arg obj;
var anc;
return filter anc in (.ancestors()) where (anc.has_ancestor(obj));
};
public method .ancestors_to(): nooverride {
arg checked, levels, maxlev;
var c, list;
list = [this()];
levels++;
// check parents
if ((!maxlev) || (maxlev != levels)) {
for c in (.parents()) {
list += [c.ancestors_to(checked, levels, maxlev)];
checked = checked.setadd(c);
}
}
return list;
};
public method .ancestry(): nooverride {
arg gen;
var i, out;
out = [];
if (type(gen) == 'objnum) {
for i in (.ancestors()) {
if (i.has_ancestor(gen))
out += [i];
}
return out;
}
if (gen != 0) {
for i in (.parents())
out = out.union(i.ancestry(gen - 1));
}
return out;
};
public method .as_this_run() {
arg obj, method, args;
if (!((caller() == $scheduler) || (caller().is($http_interface))))
throw(~perm, "Sender not allowed to gain access to object perms.");
return (> obj.(method)(@args) <);
};
public method .build_name() {
arg @args;
var output, type, part, rval;
output = "";
for part in (args) {
type = type(part);
if (type == 'list)
output += (| .(part[1])(@part.subrange(2)) |) || "";
else if (type == 'string)
output += part;
}
return output;
};
public method .change_manager(): nooverride {
arg new;
var old;
if ((caller() != definer()) && (!($sys.is_system(sender()))))
throw(~perm, "You must have system privileges to change the manager.");
(> $sys.touch() <);
if (type(new) != 'objnum)
throw(~invarg, "Managers must be given as a single dbref.");
old = .manager();
manager = new;
old.del_managed_obj();
new.add_managed_obj();
};
root method .change_parents(): nooverride {
arg parents;
var old_a, old_p, a, obj, new_a, definer, branch, method, str;
if (!parents)
throw(~noparents, "Objects must have at least 1 parent");
// remember a few things
old_a = setremove(ancestors(), this());
old_p = .parents();
branch = (.descendants()) + [this()];
// build the new ancestors list by hand, so we can figure out what is
// changing, and uninitialize those ancestors that are going away
new_a = [];
for obj in (parents)
new_a = new_a.union(obj.ancestors());
// uninit any ancestors going away
for a in (old_a.set_difference(new_a)) {
// call this ancestor's uninit on the obj and all its children
method = tosym("uninit_" + (a.objname()));
if ((definer = (| find_method(method) |))) {
if (definer != a) {
// scream madly and run around--everybody should know this
str = "UNINIT ERROR: uninit method for " + a;
str += (" in wrong place (" + definer) + ")";
(| (definer.manager()).tell(str) |);
(| (a.manager()).tell(str) |);
(| sender().tell(str) |);
continue;
}
// call the uninit method on this object only
catch any {
.(method)();
} with {
// try and let somebody know they made a boo-boo somewhere
str = ((("UNINIT ERROR " + obj) + "<") + this()) + ">:";
(| (.manager()).tell(str) |);
(| (.manager()).tell_traceback(traceback()) |);
(| sender().tell(str) |);
(| sender().tell_traceback(traceback()) |);
(| sender().tell("Continuing chparent..") |);
}
}
// cleanup any old obj vars left lying around
// if (branch)
// $sys.clear_definer_vars(a, branch);
$sys.clear_definer_vars(a, [this()]);
refresh();
}
// make the change
(> chparents(parents) <);
// init anybody new to the family
for a in (ancestors().set_difference(old_a)) {
method = tosym("init_" + (a.objname()));
if ((definer = (| find_method(method) |))) {
if (definer != a) {
// scream madly and run around--everybody should know this
// (damn inlaws, can't ever get things right)
str = "INIT ERROR: uninit method for " + a;
str += (" in wrong place (" + definer) + ")";
(| (definer.manager()).tell(str) |);
(| (a.manager()).tell(str) |);
(| sender().tell(str) |);
continue;
}
// introduce the new ancestor
catch any {
.(method)();
} with {
// try and let somebody know they made a boo-boo somewhere
str = ((("INIT ERROR " + obj) + "<") + this()) + ">:";
(| (.manager()).tell(str) |);
(| (.manager()).tell_traceback(traceback()) |);
(| sender().tell(str) |);
(| sender().tell_traceback(traceback()) |);
(| sender().tell("Continuing chparent..") |);
}
}
refresh();
}
};
public method .children(): nooverride {
return children();
};
public method .chparents() {
arg @parents;
var parent, cur;
if (!(| .perms(sender(), 'manager) |))
(> .perms(caller(), $root, $sys) <);
(> $sys.touch() <);
if (!parents)
throw(~noparents, "There must be at least 1 parent for each object.");
// Notify new parents of impending change.
cur = parents();
for parent in (parents) {
if (!(parent in cur))
(> parent.will_inherit(sender()) <);
}
// Everything's okay, go ahead and try it.
.change_parents(parents);
};
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) |);
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 .clear_setting(): nooverride {
arg name, definer;
var info, args;
(caller() == definer()) || (> .perms(sender()) <);
info = (> definer.setting_info(name) <);
if (dict_contains(info, 'clear)) {
args = sublist(info['clear], 2);
(> .((info['clear])[1])(name) <);
} else if (settings && dict_contains(settings, name)) {
settings = dict_del(settings, name);
if (!settings)
clear_var('settings);
}
};
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) |);
}
};
root method .core_root() {
(| clear_var('child_index) |) || (child_index = 0);
};
public method .corify(): nooverride {
var d;
if (sender() != $sys)
throw(~sysonly, "This should only be called by $sys.make_core().");
// reverse engineer it--if coreify_<object> exists,
// call it on this object and all of its descendants.
for d in (((.descendants()).setremove($sys)) + [$sys]) {
.corify_descendants_of(d);
refresh();
}
};
private method .corify_descendants_of(): nooverride {
arg obj;
var d, name, l;
name = (| tosym("core_" + (obj.objname())) |);
catch ~methodnf {
if ((> obj.find_method(name) <) != obj) {
$sys.log(((("** Coremethod for " + obj) + " in wrong place (on ") + (obj.find_method(name))) + ") **");
return;
}
} with {
return;
}
for d in ([obj] + (obj.descendants())) {
catch any {
(> d.(name)() <);
} with {
$sys.log(((("** ERROR encountered in " + d) + ".") + name) + "():");
for l in ($parse_lib.traceback(traceback()))
$sys.log(l);
}
refresh();
}
};
public method .created_on(): nooverride {
return created_on;
};
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, (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 .debug() {
arg @stuff;
var x, line, mngr, meth, stack;
stack = stack();
meth = (| (((stack[2])[3]) + "() line ") + ((stack[2])[4]) |);
if (meth)
line = ((("DEBUG " + sender()) + ".") + meth) + ": ";
else
line = ("DEBUG " + sender()) + ": ";
for x in (stuff)
line = (line + " ") + toliteral(x);
(| (.manager()).tell(line) |);
};
public method .define_setting(): nooverride {
arg name, @info;
var i;
(> .perms(sender()) <);
if (info) {
info = info[1];
for i in (info) {
if (!(> .valid_setting_attr(@i) <))
info = dict_del(info, i[1]);
}
} else {
info = #[];
}
if ((.all_defined_settings()).contains(name))
throw(~setexists, ("Setting \"" + name) + "\" is already defined.");
if (!($code_lib.valid_setting_id(name)))
throw(~setbad, ("Setting name \"" + name) + "\" is unacceptable.");
defined_settings = (.defined_settings()).add(name, info);
return defined_settings[name];
};
public method .defined_settings(): nooverride {
return defined_settings || #[];
};
public method .del_flag(): nooverride {
arg flag;
(> .perms(sender(), 'manager) <);
(> $sys.touch('coreok) <);
if ((flag == 'core) && (!($sys.writable_core())))
throw(~perm, this() + " is a core object, and the core isn't writable.");
// let them add any flag they want
flags = (flags || []).setremove(flag);
};
root method .del_managed_obj(): nooverride {
managed = (managed || []).setremove(sender());
if (!managed)
clear_var('managed);
};
public method .del_method() {
arg name;
(> .perms(sender()) <);
(> del_method(name) <);
};
public method .del_parent() {
arg parent;
var parents;
(> .perms(sender(), 'manager) <);
(> $sys.touch() <);
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);
(> .change_parents(parents) <);
};
public method .del_trusted(): nooverride {
arg obj;
(> .perms(sender(), 'manager) <);
(> $sys.touch('coreok) <);
trusted = (trusted || []).setremove(obj);
if (!trusted)
clear_var('trusted);
obj.del_trusted_obj();
};
root method .del_trusted_obj(): nooverride {
trusted_by = (trusted_by || []).setremove(sender());
if (!trusted_by)
clear_var('trusted_by);
};
public method .del_var(): nooverride {
arg name;
var n, obj;
(caller() == definer()) || (> .perms(sender()) <);
(> $sys.touch() <);
// try and clear the variable on all of the descendants, before deleting
// the variable...
(> .clear_variable(name) <);
// now delete the variable
(> del_var(name) <);
};
root method .del_writable_obj(): nooverride {
writes = (writes || []).setremove(sender());
if (!writes)
clear_var('writes);
};
public method .del_writer(): nooverride {
arg obj;
(caller() == definer()) || (> .perms(sender(), 'manager) <);
writers = (writers || []).setremove(obj);
(> $sys.touch('coreok) <);
if (!writers)
(| clear_var('writers) |);
obj.del_writable_obj();
};
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()) {
pause();
d = dict_add(d, child, 1);
}
pause();
}
return dict_keys(d);
};
public method .descendants_to(): nooverride {
arg checked, levels, maxlev;
var c, list;
list = [this()];
levels++;
// check parents
if ((!maxlev) || (maxlev != levels)) {
for c in (.children()) {
list += [c.descendants_to(checked, levels, maxlev)];
checked = checked.setadd(c);
}
}
return list;
};
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) |);
destroy();
};
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];
};
public method .examine() {
return [];
};
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 .flags(): nooverride {
return flags || [];
};
public method .format_descendants() {
arg ind, done, depth, max, yes, no, above;
var c, anc, list, id, perms, f, show, myflags;
show = 1;
myflags = .flags();
if (yes) {
for f in (yes) {
if (f in myflags) {
show = 1;
break;
} else {
show = 0.0;
}
}
}
for f in (.flags()) {
if (no && (f in no)) {
show = 0;
break;
}
}
if (show) {
id = ((ind + this()) + " ") + ($object_lib.see_perms(this()));
for anc in (dict_keys(done)) {
if (has_ancestor(anc))
return above ? [id + " (ABOVE)"] : [];
}
if (listlen(parents()) > 1)
id += " (MI)";
list = [id];
} else {
list = [];
}
ind += " ";
depth++;
// check children
if ((!max) || (max != depth)) {
for c in (children()) {
list += c.format_descendants(ind, done, depth, max, yes, no, above);
done = dict_add(done, c, 1);
pause();
}
}
return list;
};
public method .format_help_node_setting() {
arg node;
if (node)
return node.namef('ref);
return "";
};
public method .format_setting(): nooverride {
arg name, definer, value;
var i, args;
i = definer.setting_info(name);
if (dict_contains(i, 'format)) {
args = sublist(i['format], 2);
catch ~methodnf
return (> .((i['format])[1])(value, @args) <);
with
return (> $settings.((i['format])[1])(value, @args) <);
}
if (type(value) == 'objnum)
return value.namef('ref);
else
return "" + value;
};
public method .generations(): nooverride {
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 .get_default_setting(): nooverride {
arg name;
return settings[name];
};
public method .get_local_setting(): nooverride {
arg name, definer;
var i;
i = definer.setting_info(name);
if (dict_contains(i, 'access))
(> .((i['access])[1])(name, sender(), caller(), @sublist(i['access], 2)) <);
return (| settings[name] |) || (> definer.get_default_setting(name) <);
};
public method .get_obj_suffix() {
arg @suffix;
var objname, tmp;
// Figure out the suffix from the arguments and child index.
[(suffix ?= 0)] = suffix;
if (suffix) {
// so they dont confuse child_index:
if (suffix.is_numeric())
throw(~perm, "You cannot specify a numeric suffix.");
// so we get correct symbols & it is always lowercase:
suffix = lowercase(strsed(suffix, "[^a-z0-9_]+", "", "g"));
} else {
// get the next valid objname
objname = tostr((| .objname() |) || "unknown");
tmp = tosym(objname);
while ((| lookup(tmp) |)) {
child_index++;
tmp = tosym((objname + "_") + tostr(child_index));
}
suffix = tostr(child_index);
}
return suffix;
};
public method .get_quota(): nooverride {
arg @args;
return quota;
};
public method .get_setting(): nooverride {
arg name, definer;
var i;
i = definer.setting_info(name);
if (dict_contains(i, 'access))
(> .((i['access])[1])(name, sender(), caller(), @sublist(i['access], 2)) <);
if (dict_contains(i, 'get))
return (> .((i['get])[1])(name, definer, @sublist(i['get], 2)) <);
catch any
return settings[name];
with
return (> definer.get_default_setting(name) <);
};
public method .get_setting_attr(): nooverride {
arg name, attr;
return (defined_settings[name])[attr];
};
public method .has_ancestor(): nooverride {
arg obj;
return (> has_ancestor(obj) <);
};
public method .has_flag(): nooverride {
arg flag, @sender;
[(sender ?= sender())] = sender;
if (flag == 'core)
return flag in (.flags());
return (flag in (.flags())) || (.trusts(sender));
};
public method .help_node() {
arg @args;
return help_node;
};
public method .hname() {
arg @args;
return ((("<a href=\"/bin/display?" + this()) + "\">") + this()) + "</a>";
};
root method .init_root(): nooverride {
.change_manager(this());
flags = ['variables, 'methods, 'code];
created_on = time();
};
public method .initialize(): nooverride {
var ancestors, pos, len, method, a, def;
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] {
refresh();
a = ancestors[len - pos];
if (!(method = (| tosym("init_" + tostr(a.objname())) |)))
continue;
if ((def = (| find_method(method) |))) {
if (def != a) {
(| (def.manager()).tell(((("Initialization method for " + a) + " in wrong place (") + find_method(method)) + ")") |);
} else {
catch any {
.(method)();
} with {
if (def) {
(| (def.manager()).tell(((("INIT ERROR " + this()) + "<") + def) + ">:") |);
(| (def.manager()).tell_traceback(traceback()) |);
}
}
}
}
}
inited = 1;
};
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 .is_of(): nooverride {
arg obj;
return obj in ancestors();
};
public method .is_writable_by(): nooverride {
arg obj;
return (| obj in (.writers()) |) || ($sys.is_system(obj));
};
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) <);
};
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) != 0) {
i = a.method_info(m);
methods = methods.add_elem(i[5], [a, m, @i]);
}
}
}
return methods;
};
public method .managed(): nooverride {
return managed || [];
};
public method .manager(): nooverride {
return manager || $reaper;
};
public method .match_children() {
arg string;
var children, child_names, c;
children = .children();
child_names = children.mmap('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 .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 .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 .method_bytecode() {
arg method;
return (> method_bytecode(method) <);
};
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_info(): nooverride {
arg @args;
return (> method_info(@args) <);
};
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 .name() {
arg @args;
return tostr(this());
};
public method .namef() {
arg type;
return tostr(this());
};
public method .new() {
arg @suffix;
var obj, tmp, objname, mngr, na;
// this difference between this and .spawn() is that you
// can override this method to return a frob instead.
(> .will_spawn(sender()) <);
suffix = (> .get_obj_suffix(@suffix) <);
return $sys.spawn_sender(suffix, sender());
};
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();
};
public method .objname(): nooverride {
return (> objname() <);
};
public method .objnum(): nooverride {
return objnum();
};
public method .parents(): nooverride {
return parents();
};
public method .parse_help_node_setting() {
arg value, @args;
if (!value)
return 0;
value = (> $object_lib.to_dbref(value) <);
if (!(value.is($help_node)))
throw(~perm, (value.namef('ref)) + " is not a $help_node.");
return value;
};
public method .perms(): nooverride {
arg what, @args;
var flag;
if (!args)
args = ['writer];
if (type(args[1]) == 'symbol) {
switch (args[1]) {
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);
case 'command:
if ((what != $user) && (what != $body))
throw(~perm, what + " is not a valid command invoking object.");
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: ") + ((args.mmap('namef, 'ref)).to_english()), what);
}
};
public method .quota(): nooverride {
return quota;
};
public method .quota_byte_usage(): nooverride {
var total, obj;
for obj in (.managed()) {
if (!valid(obj))
continue;
total += obj.size();
}
return total;
};
public method .quota_exempt(): nooverride {
return quota_exempt;
};
public method .quota_valid(): nooverride {
if (quota_exempt)
return 1;
if (!(.is($user)))
return 0;
return (.quota_byte_usage()) < (.quota());
};
public method .rename_method() {
arg now, new;
(> .perms(sender()) <);
(> $sys.touch() <);
return (> rename_method(now, new) <);
};
public method .set_flags(): nooverride {
arg new_flags;
(> .perms(sender(), 'manager) <);
(> $sys.touch() <);
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;
};
protected method .set_help_node_setting() {
arg name, definer, value;
if (!value)
(| clear_var('help_node) |);
else
help_node = value;
};
public method .set_method_access(): nooverride {
arg method, state;
if (!(.is_writable_by(sender())))
throw(~perm, (sender() + " cannot write to ") + this());
(> $sys.touch() <);
return (> set_method_access(method, state) <);
};
public method .set_method_flags(): nooverride {
arg method, flags;
if (!(.is_writable_by(sender())))
throw(~perm, (sender() + " cannot write to ") + this());
(> $sys.touch() <);
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_objname() {
arg objname;
(> .perms(sender()) <);
if (.has_flag('core))
throw(~perm, this() + " is a core object; you cannot change its object name!");
(> $sys.touch() <);
// 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) <);
};
public method .set_quota(): nooverride {
arg value;
(> .perms(caller(), $user, @$sys.system(), $root, $admin) <);
quota = value;
};
public method .set_quota_exempt(): nooverride {
arg bool;
(> .perms(caller(), $user, @$sys.system(), $root) <);
if (bool)
quota_exempt = 1;
else
(| clear_var('quota_exempt) |);
};
public method .set_setting(): nooverride {
arg name, definer, value;
var i, args;
(> .perms(sender()) <);
i = (> definer.setting_info(name) <);
if (dict_contains(i, 'parse)) {
args = sublist(i['parse], 2);
catch ~methodnf
value = (> .((i['parse])[1])(value, @args) <);
with
value = (> $settings.((i['parse])[1])(value, @args) <);
}
if (dict_contains(i, 'set))
(> .((i['set])[1])(name, definer, value, @sublist(i['set], 2)) <);
else
settings = dict_add(settings || #[], name, value);
};
public method .set_setting_attr(): nooverride {
arg name, attr, value;
var info;
(> .perms(sender()) <);
if ((!defined_settings) || (!dict_contains(defined_settings, name)))
throw(~setnf, (("Setting \"" + name) + "\" is not defined on ") + this());
if (value && (!(> .valid_setting_attr(attr, value) <)))
return;
info = defined_settings[name];
if (!value)
info = dict_del(info, attr);
else
info = dict_add(info, attr, value);
defined_settings = dict_add(defined_settings, name, info);
};
public method .setting_definer(): nooverride {
arg name;
var a;
for a in (ancestors()) {
if (dict_contains(a.defined_settings(), name))
return a;
}
throw(~setnf, ("Setting \"" + name) + "\" is not defined.");
};
public method .setting_info(): nooverride {
arg setting;
return defined_settings[setting];
};
public method .settings(): nooverride {
(> .perms(sender()) <);
return settings || #[];
};
public method .size(): nooverride {
arg @args;
[(args ?= 'int)] = args;
switch (args) {
case 'string:
return tostr(size());
case 'english:
return size().to_english();
default:
return size();
}
};
public method .spawn(): nooverride {
arg @suffix;
var obj, tmp, objname, mngr, na;
(> .will_spawn(sender()) <);
suffix = (> .get_obj_suffix(@suffix) <);
return $sys.spawn_sender(suffix, sender());
};
public method .test_method() {
return "from root";
};
public method .trusted(): nooverride {
arg @literal;
if (literal)
return trusted || [];
return (trusted || []) + (.writers());
};
public method .trusted_by(): nooverride {
return trusted_by || [];
};
public method .trusts(): nooverride {
arg obj;
return (| obj in (.trusted()) |) || ((obj in [$scheduler, $directories]) || ($sys.is_system(obj)));
};
public method .undefine_setting(): nooverride {
arg name;
var d;
(> .perms(sender()) <);
if (!((.defined_settings()).contains(name)))
throw(~setnf, (("Setting \"" + name) + "\" is not defined by ") + this());
// clear it on all descendants, then us
for d in ((.descendants()) + [this()]) {
d._clear_setting(name);
pause();
}
// bye bye
defined_settings = dict_del(defined_settings, name);
if (!defined_settings)
clear_var('defined_settings);
};
root method .uninit_root(): nooverride {
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());
}
// tell $sys we are going away
$sys.sender_going_away();
};
public method .uninitialize(): nooverride {
arg @destroyed;
var a, v, d, definer, method, descendants, str;
(> .perms(caller(), $root, $sys) <);
destroyed = destroyed ? 1 : 0;
descendants = (.descendants()) + [this()];
// call [ancestors...].uninit_ancestor() on the object being destroyed
for a in (ancestors()) {
method = tosym("uninit_" + (a.objname()));
if ((definer = (| find_method(method) |))) {
if (definer != a) {
// scream madly and run around--everybody should know this
str = "UNINIT ERROR: uninit method for " + a;
str += (" in wrong place (" + definer) + ")";
(| (definer.manager()).tell(str) |);
(| (a.manager()).tell(str) |);
(| sender().tell(str) |);
continue;
}
catch any {
.(method)();
} with {
// try and let somebody know they made a boo-boo somewhere
str = ((("UNINIT ERROR " + this()) + "<") + definer) + ">:";
if (definer) {
(| (definer.manager()).tell(str) |);
(| (definer.manager()).tell_traceback(traceback()) |);
}
(| sender().tell(str) |);
(| sender().tell_traceback(traceback()) |);
(| sender().tell("Continuing uninit..") |);
}
}
refresh();
}
// if we have descendants, clean anything from the object being dested
method = tosym("uninit_" + (.objname()));
if ((definer = (| find_method(method) |))) {
if (definer != this()) {
// scream madly and run around--everybody should know this
str = "UNINIT ERROR: uninit method for " + a;
str += (" in wrong place (" + definer) + ")";
(| (definer.manager()).tell(str) |);
(| (.manager()).tell(str) |);
(| sender().tell(str) |);
} else {
for d in (descendants) {
catch any {
d.(method)();
} with {
// try and let somebody know they made a boo-boo somewhere
str = ((("UNINIT ERROR " + d) + "<") + this()) + ">:";
(| (.manager()).tell(str) |);
(| (.manager()).tell_traceback(traceback()) |);
(| sender().tell(str) |);
(| sender().tell_traceback(traceback()) |);
(| sender().tell("Continuing uninit..") |);
}
refresh();
}
}
}
// clear vars
if ((!destroyed) && descendants)
$sys.clear_definer_vars(this(), descendants);
};
public method .valid_setting_attr(): nooverride {
arg name, value;
if (!(name in ['get, 'set, 'parse, 'clear, 'format, 'access]))
throw(~setattr, "Invalid setting attribute '" + name);
if (!value)
return 0;
else if (type(value) != 'list)
throw(~setattr, "Setting attribute must be a list");
else if (type(value[1]) != 'symbol)
throw(~setattr, "Setting attribute[1] is not a symbol");
return 1;
};
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 ((data().to_list()).reverse()) {
if (valid(pp[1]) && ((pp[1]) in ancs)) {
for p in (pp[2]) {
if (tostr(p[1]).(match)(fltr) != 0)
data += [[pp[1], @p]];
}
}
}
return data;
};
public method .variables(): nooverride {
if (!(.has_flag('variables, sender())))
throw(~perm, (sender() + " doesn't have permission to find methods on ") + this());
return variables();
};
public method .will_inherit() {
arg who;
if (this() in (sender().parents()))
throw(~perm, ((sender() + " already has ") + this()) + " as a parent.");
if ((!(.has_flag('fertile, sender()))) && (!(.trusts(who))))
throw(~perm, ((this() + " refuses to be parent of ") + sender()) + ".");
};
public method .will_spawn(): nooverride {
arg who;
if (!(.has_flag('fertile, who)))
throw(~perm, "Not fertile or readable.");
// available quota?
if (!(who.quota_valid()))
throw(~quota, "Sender does not have the available quota");
};
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 .writes(): nooverride {
(> .perms(sender(), 'trusts) <);
return writes || [];
};