new object $programmer: $storyteller, $dmi_data_ui; var $channel_ui active_channels = #[]; var $channel_ui channel_dict = #[]; var $command_aliases command_aliases = []; var $described prose = []; var $has_commands local = \ #[["@id", [["@id", "*", "@id <any>", 'id_cmd, #[[1, ['any, []]]]]]],\ ["@which",\ [["@which", "*", "@which <any>", 'which_cmd, #[[1, ['any, []]]]]]],\ ["@eval",\ [["@eval", "*", "@eval <any>", 'eval_cmd, #[[1, ['any, []]]]]]],\ ["@add-c?ommand|@ac",\ [["@add-c?ommand|@ac", "*", "@add-c?ommand|@ac <any>", 'add_command_cmd, #[[1, ['any, []]]]]]],\ ["@del-c?ommand|@dc",\ [["@del-c?ommand|@dc", "*", "@del-c?ommand|@dc <any>", 'del_command_cmd, #[[1, ['any, []]]]]]],\ ["@join",\ [["@join", "*", "@join <any>", 'join_cmd, #[[1, ['any, []]]]]]],\ ["@chpar?ents",\ [["@chpar?ents", "*", "@chpar?ents <any>", 'chparents_cmd, #[[1, ['any, []]]]]]],\ ["@add-s?hortcut|@as",\ [["@add-s?hortcut|@as", "*", "@add-s?hortcut|@as <any>", 'add_shortcut_cmd, #[[1, ['any, []]]]]]],\ ["@del-m?ethod|@delm?ethod|@dm",\ [["@del-m?ethod|@delm?ethod|@dm", "*", "@del-m?ethod|@delm?ethod|@dm <objref>", 'del_method_cmd, #[[1, ['objref, []]]]]]],\ ["@rehash",\ [["@rehash", "", "@rehash", 'rehash_cmd, #[]]]], ["@trace-method|@trace", [["@trace-method|@trace", "*", "@trace-method|@trace <objref>", 'trace_method_cmd, #[[1, ['objref, []]]]]]],\ ["@ledit",\ [["@ledit", "*", "@ledit <objref: +e?dited>", 'local_edit_cmd, #[[1, ['objref_opt, ["e?dited"]]]]]]],\ ["@program",\ [["@program", "*", "@program <objref: +w?arnings +e?dited=1 +a?ccess=1 +f?lags=1>", 'program_cmd, #[[1, ['objref_opt, ["w?arnings", "e?dited", "a?ccess", "f?lags"]]]]], ["@program", "* with *", "@program <objref: +w?arnings +e?dited=1 +a?ccess=1 +f?lags=1> with <any>", 'program_cmd, #[[1, ['objref_opt, ["w?arnings", "e?dited", "a?ccess", "f?lags"]]]], [3, ['any, []]]]]], ["@del-v?ariable|@dv", [["@del-v?ariable|@dv", "*", "@del-v?ariable|@dv <objref>", 'del_var_cmd, #[[1, ['objref, []]]]]]],\ ["@show",\ [["@show", "*", "@show <objref: +c?hop>", 'show_cmd, #[[1, ['objref_opt, ["c?hop"]]]]]]],\ ["@mv|@move|@cp|@copy",\ [["@mv|@move|@cp|@copy", "*", "@mv|@move|@cp|@copy <objref:+c?omment=1>", 'move_cmd, #[[1, ['objref_opt, ["c?omment"]]]]]]],\ ["@del-s?hortcut|@ds",\ [["@del-s?hortcut|@ds", "*", "@del-s?hortcut|@ds <any>", 'del_shortcut_cmd, #[[1, ['any, []]]]]]],\ ["@add-p?arent|@ap",\ [["@add-p?arent|@ap", "*", "@add-p?arent|@ap <any>", 'add_parent_cmd, #[[1, ['any, []]]]]]],\ ["@del-p?arent|@dp",\ [["@del-p?arent|@dp", "*", "@del-p?arent|@dp <any>", 'del_parent_cmd, #[[1, ['any, []]]]]]],\ ["@grep",\ [["@grep", "*", "@grep <any:+d?escend +f?ull +l?ist +r?eplace-with=1>", 'grep_cmd, #[[1, ['any_opt, ["d?escend", "f?ull", "l?ist", "r?eplace-with"]]]]]]],\ ["@chmod|@mmod|@omod|@chflag?s",\ [["@chmod|@mmod|@omod|@chflag?s", "*", "@chmod|@mmod|@omod|@chflag?s <any>", 'chmod_cmd, #[[1, ['any, []]]]]]],\ ["@dump",\ [["@dump", "*", "@dump <any: +t?extdump +m?ethods +v?ariables +h?eader>", 'dump_cmd, #[[1, ['any_opt, ["t?extdump", "m?ethods", "v?ariables", "h?eader"]]]]]]],\ ["@list",\ [["@list", "*", "@list <objref: +n?umbers +t?extdump>", 'list_cmd, #[[1, ['objref_opt, ["n?umbers", "t?extdump"]]]]]]],\ ["@add-v?ariable|@av",\ [["@add-v?ariable|@av", "*", "@add-v?ariable|@av <any>", 'add_var_cmd, #[[1, ['any, []]]]]]],\ ["@hl?ist|@help-list",\ [["@hl?ist|@help-list", "*", "@hl?ist|@help-list <any>", 'help_list_cmd, #[[1, ['any, []]]]]]],\ ["@hw?rite|@help-write",\ [["@hw?rite|@help-write", "*", "@hw?rite|@help-write <any>", 'help_write_cmd, #[[1, ['any, []]]]]]],\ ["@spawn",\ [["@spawn", "*", "@spawn <any>", 'spawn_cmd, #[[1, ['any, []]]]]]],\ ["@ancestors",\ [["@ancestors", "*", "@ancestors <any>", 'ancestors_cmd, #[[1, ['any, []]]]]]],\ ["@nh?n|@new-help|@new-help-node",\ [["@nh?n|@new-help|@new-help-node", "*", "@nh?n|@new-help|@new-help-node <any:+n?amed=1 +o?bjname=1 +i?ndex=1>", 'new_help_node_cmd, #[[1, ['any_opt, ["n?amed", "o?bjname", "i?ndex"]]]]]]],\ ["@config-set?ting|@configure-set?ting",\ [["@config-set?ting|@configure-set?ting", "*", "@config-set?ting|@configure-set?ting <any>", 'configure_setting_cmd, #[[1, ['any, []]]]]]],\ ["@def-set?ting|@define-set?ting",\ [["@def-set?ting|@define-set?ting", "*", "@def-set?ting|@define-set?ting <any>", 'define_setting_cmd, #[[1, ['any, []]]]]]],\ ["@undef-set?ting|@undefine-set?ting",\ [["@undef-set?ting|@undefine-set?ting", "*", "@undef-set?ting|@undefine-set?ting <any>", 'undefine_setting_cmd, #[[1, ['any, []]]]]]],\ ["@descend?ants",\ [["@descend?ants", "*", "@descend?ants <objref:+a?ll +r?edundant +o?nly +n?ot>", 'descendants_cmd, #[[1, ['objref_opt, ["a?ll", "r?edundant", "o?nly", "n?ot"]]]]]]],\ ["@d?isplay",\ [["@d?isplay", "*", "@d?isplay <objref: +c?hop +g?enerations>", 'display_cmd, #[[1, ['objref_opt, ["c?hop", "g?enerations"]]]]]]],\ ["@chman?age",\ [["@chman?age", "*", "@chman?age <any>", 'chmanage_cmd, #[[1, ['any, []]]]]]]]; var $has_commands shortcuts = #[[";*", ['eval_cmd, ["eval", 1]]]]; var $has_name name = ['prop, "Generic Programmer", "Generic Programmer"]; var $located location = $body_cave; var $located obvious = 1; var $location contents = []; var $mail_list last_letter = 0; var $mail_list letters = #[]; var $mail_list letters_index = #[]; var $mail_list mail = []; var $mail_list notify = [$programmer]; var $mail_list readers = []; var $mail_list senders = 1; var $mail_ui current = #[['location, 0], ['list, $programmer]]; var $mail_ui subscribed = #[[$programmer, [791485891, 0]]]; var $programmer eval_offset = 0; var $programmer eval_prefix = 0; var $programmer eval_tick_offset = 0; var $root created_on = 796268969; var $root defined_settings = #[["match-with", #[['parse, ['parse_match_with]]]], ["match-default", #[]], ["@program-options", #[]], ["@list-options", #[]]]; var $root flags = ['methods, 'code, 'core, 'command_cache, 'variables]; var $root inited = 1; var $root managed = [$programmer]; var $root manager = $programmer; var $root quota = 75000; var $root settings = #[["match-default", "*"], ["home", $body_cave], ["@list-options", ""], ["@program-options", ""], ["match-with", 'match_pattern], ["extended-parsers", []]]; var $thing gender = $gender_neuter; var $user connected_at = 0; var $user connections = []; var $user formatter = $plain_format; var $user last_command_at = 0; var $user modes = #[]; var $user parsers = [$command_parser]; var $user password = "*"; var $user task_connections = #[]; private method ._def_setcmd_opt() { arg opt, type, opts, dict; var i, value, m; if (!(i = opt in (opts.slice(1)))) return dict; value = (opts[i])[4]; if (!value) throw(~stop, ("No value for option \"" + opt) + "\"."); catch any { switch (type) { case 'symbol: value = [(> value.to_symbol() <)]; case 'data_list: value = (> fromliteral(value) <); if (type(value) != 'list) value = [value]; } } with { rethrow(~stop); } opt = opt.strip("?"); if ((m = match_pattern(opt, "*-args"))) { opt = tosym(m[1]); if (!dict_contains(dict, opt)) throw(~stop, ((("Arguments defined for " + opt) + " without defining ") + opt) + " method."); value = [(dict[opt])[1], @value]; return dict_add(dict, opt, value); } else { return dict_add(dict, tosym(opt), value); } }; protected method ._display_methods() { arg obj, info, chop, f; var type, types, line, out, m, len; len = .linelen(); out = []; for type in (info.keys()) { line = (tostr(type).capitalize()) + " Methods"; if (f) line += (" matching \"" + f) + "\""; out += [line + ":"]; for m in (info[type]) { line = strfmt("%5l %4r %l.%l(%l)", $object_lib.parse_method_flags(m[8]), m[6], ((m[1]) != obj) ? (m[1]) : "", m[2], m[3]); if (chop) line = line.chop(len); out += [line]; refresh(); } } return out; }; protected method ._display_variables() { arg obj, info, chop, f; var line, i, len, out, fmt; len = .linelen(); line = "Object Variables"; if (f) line += (" matching \"" + f) + "\""; out = [line]; for i in (info.reverse()) { line = strfmt(" %s,%s: %d", ((i[1]) != obj) ? (i[1]) : "", i[2], i[3]); if (chop) line = line.chop(len); out += [line]; refresh(); } return out; }; protected method ._edit_method_callback() { arg code, client_data; var errors, edited, object, method, warns, sender; (> .perms(caller(), $editor_reference) <); [object, method] = client_data; sender = this(); code += [(("// $#Edited: " + ($time.format("%d %h %y %H:%M"))) + " ") + sender]; errors = object.add_method(code, method); if (errors) return ['failure, errors]; warns = (> $code_lib.verify_code(code, method, 1) <); return ['success, [warns + ["Method compiled."]]]; }; protected method ._list_method() { arg obj, method, @args; var code, opt, flags, f; [(args ?= 0), (opt ?= "")] = args; code = obj.list_method(method); flags = obj.method_flags(method); if (args) { code = code.numbered_text(); return ([(((((("-- " + (obj.method_access(method))) + " method ") + obj) + ".") + method) + "()") + (flags ? (": " + (flags.join(", "))) : "")] + code) + ["--"]; } else { return ([$object_lib.format_method_header(obj, method, opt, flags, obj.method_access(method))] + (code.prefix(" "))) + ["."]; } }; protected method ._move_method() { arg remove, fobj, fname, tobj, tname, comment; var code, line, result, access, flags; if ((fobj == tobj) && remove) { if ((| tobj.find_method(tname) |) == tobj) tobj.del_method(tname); return (> fobj.rename_method(fname, tname) <); } code = (> fobj.list_method(fname) <); flags = (> fobj.method_flags(fname) <); access = (> fobj.method_access(fname) <); if (comment) { line = (((((((("// $#" + (remove ? "Moved" : "Copied")) + " ") + ($time.format("%d %h %y %H:%M"))) + " from ") + fobj) + ".") + fname) + "() by ") + this(); if (type(comment) == 'string) line += ": " + comment; code += [line]; } if ((> tobj.add_method(code, tname) <)) throw(~compile, "Error encountered upon moving method!"); (> tobj.set_method_flags(tname, flags) <); (> tobj.set_method_access(tname, access) <); if (remove) (> fobj.del_method(fname) <); }; public method ._move_variable() { arg remove, fobj, fname, tobj, tname, comment; var value, line, result, tmp; value = (> fobj.eval([("return " + fname) + ";"]) <); if ((value[1]) != 'result) throw(~eval, "An error was encountered upon evaluation."); value = value[2]; (> tobj.add_var(tname, value) <); if (remove) (> fobj.del_var(fname) <); }; public method ._show_methods() { arg obj, f, match, chop; var methods, types, m, t, out; types = #[]; for m in (obj.methods()) { if (tostr(m).(match)(f) != 0) types = types.add_elem(obj.method_access(m), ((("." + m) + "(") + ((obj.method_info(m))[1])) + ")"); } // hard-listing the types guarantee's their order out = []; for t in (['root, 'driver, 'public, 'protected, 'private, 'frob]) { if (!(types.contains(t))) continue; out += [(((tostr(t).capitalize()) + " methods matching \"") + f) + "\":"]; for m in (types[t]) out += [" " + m]; } return out; }; public method ._show_variables() { arg obj, f, match, chop; var parent, out, v, line, len; out = []; len = .linelen(); for parent in (obj.data()) { if (valid(parent[1])) { out += [(((parent[1]) + " variables matching \"") + f) + "\":"]; if ((parent[1]).has_flag('variables, this())) { for v in (parent[2]) { if (tostr(v[1]).(match)(f) == 0) continue; line = ((" " + (v[1])) + ": ") + toliteral(v[2]); if (chop) line = line.chop(len); out += [line]; } } else { out += [" ** Permission Denied **"]; } } else { out += [($object_lib.get_name(parent[1])) + " Variables:"]; for v in (parent[2]) { if (tostr(v[1]).(match)(f) == 0) continue; line = ((" " + (v[1])) + ": ") + toliteral(v[2]); if (chop) line = line.chop(len); out += [line]; } } refresh(); } return out; }; private method ._which_cmd() { arg partial, parent, type, more; var p, cmds, cmd, def, matches; cmds = (| parent.(type)() |) || #[]; matches = []; for def in (cmds.keys()) { for cmd in (cmds[def]) { if (partial in ((cmd[3]).strip("?"))) matches += [[more, cmd[3], cmd[4]]]; } } return matches; }; protected method .add_command_cmd() { arg cmdstr, cmd, str; var ref, t, args, objref; (> .perms(caller(), 'command) <); args = str.explode_quoted(); if (listlen(args) > 2) { if ((args[2]) in ["to", "for"]) args = delete(args, 2); t = delete(args, listlen(args)).join(); objref = args.last(); } else if (listlen(args) == 2) { t = args[1]; objref = args[2]; } else { return ("Syntax: `" + cmd) + " \"template\" [to|for] <objref>"; } catch any { ref = (> $parse_lib.ref(objref) <); if ((ref[1]) != 'method) return ("The reference " + objref) + " is not for a method."; if ((!(ref[4])) || (!((ref[4]).valid_ident()))) return ((("Invalid method name " + (ref[3])) + ".") + (ref[4])) + "()."; (> (ref[2]).add_command(t, tosym(ref[4])) <); } with { return (traceback()[1])[2]; } return strfmt("Command %d added to %s.%s()", t, ref[3], ref[4]); }; protected method .add_parent_cmd() { arg cmdstr, cmd, args; var syn, obj, parent; (> .perms(caller(), 'command) <); args = args.explode(); if ((listlen(args) > 2) && ((args[2]) == "to")) args = delete(args, 2); if (listlen(args) != 2) return ("Syntax: `" + cmd) + " <parent> [to] <object>`"; parent = (> .match_env_nice(args[1]) <); obj = (> .match_env_nice(args[2]) <); catch any { (> obj.add_parent(parent) <); return ((("Added parent to " + (obj.namef('ref))) + ", parents: ") + ((obj.parents()).to_english())) + "."; } with { return (traceback()[1])[2]; } }; protected method .add_shortcut_cmd() { arg cmdstr, cmd, args; var ref, syn; (> .perms(caller(), 'command) <); args = args.explode_quoted(); syn = cmd + " \"<shortcut>\" [to] \"<command>\" [on] \"<object>\""; if ((listlen(args) == 5) && (((args[2]) == "to") && ((args[4]) == "on"))) args = [args[1], args[3], args[5]]; if (listlen(args) != 3) return ("Syntax: `" + syn) + "`"; ref = (> $parse_lib.ref(args[3]) <); if (((ref[1]) != 'method) || ((!(ref[4])) || (!(| tosym(ref[4]) |)))) return ("Invalid method reference reference \"" + (args[3])) + "\"."; catch any (> (ref[2]).add_shortcut(args[1], args[2], tosym(ref[4])) <); with return (traceback()[1])[2]; return strfmt("Added shortcut %d to command %d on %s.%s().", args[1], args[2], ref[2], ref[4]); }; protected method .add_var_cmd() { arg cmdstr, cmd, args; var ref, value; (> .perms(caller(), 'command) <); if (!args) return "Invalid obj,variable reference."; ref = (> $parse_lib.ref(args.word(1)) <); if (((ref[1]) != 'variable) || (!(ref[4]))) return "Invalid obj,variable reference."; if (" " in args) { args = substr(args, (" " in args) + 1); if (args && ((args[1]) == "=")) args = substr(args, (" " in args) + 1); if (args) { value = .eval([("return " + args) + ";"]); if ((value[1]) == 'errors) return ("Unable to parse value \"" + args) + "\"."; value = value[2]; } else { value = 0; } } else { value = 0; } catch any { (> (ref[3]).add_var(tosym(ref[4]), value) <); } with { if (error() in [~varexists, ~symbol]) return (traceback()[1])[2]; rethrow(error()); } return ((((("Object variable " + (ref[3])) + ",") + (ref[4])) + " added with value ") + value) + "."; }; protected method .ancestors_cmd() { arg cmdstr, cmd, args; var syn, obj, maxlevels, line; (> .perms(caller(), 'command) <); syn = cmd + " <obj> [<levels>]"; args = args.explode(); if (!((args.length()) in [1, 2])) return syn; obj = .match_env_nice(args[1]); if ((args.length()) == 2) { if ((args[2]) == "all") maxlevels = 0; else maxlevels = abs(toint(args[2])) + 1; } else { maxlevels = 3; } line = ("Ancestors of " + obj) + ":"; if (maxlevels) { line += tostr(maxlevels - 1); line = ((line + " level") + (((maxlevels - 1) > 1) ? "s" : "s")) + ":"; } else { line += "all levels:"; } .tell(line); .tell(obj._display_ancestors("", #[], 0, maxlevels)); .tell("---"); }; protected method .chmanage_cmd() { arg cmdstr, cmd, args; var obj, manager; (> .perms(caller(), 'command) <); args = (args.replace(" to ", " ")).explode(); if ((!args) || ((args.length()) != 2)) (> .tell_error(cmd + " <object> [to] <user>") <); obj = .match_env_nice(args[1]); manager = .match_env_nice(args[2]); if ((!(manager.is($user))) && (!(.is($admin)))) return "Sorry you can only set users as managers."; catch any (> obj.change_manager(manager) <); with return (traceback()[1])[2]; return ((("Manager on " + (obj.namef('xref))) + " changed to ") + (manager.namef('xref))) + "."; }; protected method .chmod_cmd() { arg cmdstr, cmd, args; var a, ts, t, opts, b, objs, o, precedence, ref, flags, match, m; (> .perms(caller(), 'command) <); args = args.explode_quoted(); ts = ["cod?e", "cor?e", "d?river", "fe?rtile", "fo?rked", "fr?ob", "l?ocked", "m?ethods", "na?tive", "no?override", "pri?vate", "pro?tected", "pu?blic", "r?oot", "v?ariables"]; if (!args) return ("=> Syntax: `" + cmd) + " <options> <object> [<object ..]`"; opts = #[]; objs = []; for a in (args) { if (a && ((a[1]) in ["+", "-"])) { b = (a[1]) == "+"; a = substr(a, 2); match = 0; for t in (ts) { if (match_template(a, t)) { opts = dict_add(opts, tosym(t.strip()), b); match++; break; } } if (!match) { catch ~symbol opts = dict_add(opts, tosym(a), b); with .tell(("Invalid option '" + a) + "' (non-alphanumeric characters)"); } } else { objs += [a]; } } if (!objs) return [("=> Syntax: `" + cmd) + " <options> <object> [<object ..]`", "No objects specified."]; if (!opts) return [("=> Syntax: `" + cmd) + " <options> <object> [<object ..]`", "No options specified."]; // ok, now handle it, keep precedence for their own sake for o in (objs) { catch any { ref = (| $parse_lib.ref(o) |); } with { .tell((traceback()[1])[2]); continue; } if (!precedence) { precedence = ref[1]; } else if ((ref[1]) != precedence) { .tell(((("Item '" + o) + "' is not a ") + precedence) + " reference."); .tell("All references must be the same type."); continue; } o = ref[3]; for a in (dict_keys(opts)) { catch any { switch (a) { case 'driver, 'private, 'protected, 'public, 'root, 'frob: if (precedence != 'method) { .tell((("Option " + ((opts[a]) ? "+" : "-")) + a) + " is only applicable to methods."); continue; } m = (> tosym(ref[4]) <); (> o.set_method_access(m, a) <); .tell(((("Set " + ($parse_lib.buildref(@ref))) + " access to ") + a) + "."); case 'nooverride, 'locked, 'native, 'forked: if (precedence != 'method) { .tell((("Option " + ((opts[a]) ? "+" : "-")) + a) + " is only applicable to methods."); continue; } m = (> tosym(ref[4]) <); if (opts[a]) { o.set_method_flags(m, setadd(o.method_flags(m), a)); .tell((((("Added Method Flag +" + a) + " to ") + ($parse_lib.buildref(@ref))) + ", flags: ") + (((o.method_flags(m)).prefix("+")).join())); } else { o.set_method_flags(m, setremove(o.method_flags(m), a)); .tell((((("Removed Method Flag +" + a) + " from ") + ($parse_lib.buildref(@ref))) + ", flags: ") + (((o.method_flags(m)).prefix("+")).join())); } default: if (precedence != 'object) { .tell((("Option " + ((opts[a]) ? "+" : "-")) + a) + " is only applicable to objects."); continue; } if (opts[a]) { o.add_flag(a); .tell((((("Added Object Flag +" + a) + " to ") + (o.namef('ref))) + ", flags: ") + (((o.flags()).prefix("+")).join())); } else { o.del_flag(a); .tell((((("Removed Object Flag +" + a) + " from ") + (o.namef('ref))) + ", flags: ") + (((o.flags()).prefix("+")).join())); } } } with { .tell((traceback()[1])[2]); } } refresh(); } }; protected method .chparents_cmd() { arg cmdstr, cmd, args; var syn, p, x, obj, parents, match; (> .perms(caller(), 'command) <); syn = ("Syntax: `" + cmd) + " <child> [to] <parent>, <parent>, ...`"; if ((match = match_template(args, "* to *"))) { obj = match[1]; parents = match[3]; } else if ((args = explode(args))) { if (listlen(args) == 1) return syn; obj = args[1]; parents = sublist(args, 2).join(); } else { return syn; } obj = (> .match_env_nice(obj) <); if (("," in parents) || (" and " in parents)) parents = parents.explode_english_list(); else parents = parents.explode(); if (!parents) return "No parents to change to."; parents = map p in (parents) to ((> .match_env_nice(p) <)); catch any { obj.chparents(@parents); return ((("Changed parents for " + obj) + " to ") + (parents.to_english())) + "."; } with { return (traceback()[1])[2]; } }; public method .clear_eval() { (| clear_var('eval_offset) |); }; public method .configure_setting_cmd() { arg cmdstr, cmd, args; var def_opts, opts, o, name, config, m, definer, syn, type, def, val; syn = ("Syntax: `" + cmd) + " <definer>:<setting>[=default] [options]`"; def_opts = []; for o in (["get", "set", "parse"]) def_opts += [[o, 1], [o + "-a?rgs", 1]]; def_opts += [["c?lear", 1], ["f?ormat", 1], ["a?ccess", 1], ["t?ype", 1]]; [args, opts] = $parse_lib.getopt(args, def_opts); args = join(args, " "); if (!args) { .tell(syn); for o in (def_opts.slice(1)) .tell((((" +" + o) + "=<") + ((o.strip("?")).replace("-", " "))) + ">"); return "Types can be any ColdC type and \"boolean\""; } if ((m = regexp(args, "^([^=]+) *= *(.*)$"))) [args, def] = m; else def = ""; if ((m = regexp(args, "^([^:]+) *: *([\@a-z0-9-]+)"))) [definer, name] = m; else definer = args; catch any definer = (> .match_environment(definer) <); with return (traceback()[1])[2]; if (!name) return syn; catch ~setnf definer = definer.setting_definer(name); with return (traceback()[1])[2]; // setup some default config opts based off the desired type config = #[]; if ((m = "t?ype" in (opts.slice(1)))) { type = (| ((opts[m])[4]).to_symbol() |); if ((!type) || (!($settings.is_valid_type(type)))) return "Types can be any ColdC type and " + (($settings.valid_types()).to_english("", " or ")); switch (type) { case 'boolean: config = #[['parse, ['is_boolean]], ['format, ['format_boolean]]]; case 'itemlist: // do nothing, we re-adjust things later default: config = #[['parse, ['is_type, type]]]; } } // now build default config--not the most efficient way--but cleaner config = (> ._def_setcmd_opt("get", 'symbol, opts, config) <); config = (> ._def_setcmd_opt("get-a?rgs", 'data_list, opts, config) <); config = (> ._def_setcmd_opt("set", 'symbol, opts, config) <); config = (> ._def_setcmd_opt("set-a?rgs", 'data_list, opts, config) <); config = (> ._def_setcmd_opt("parse", 'symbol, opts, config) <); config = (> ._def_setcmd_opt("parse-a?rgs", 'data_list, opts, config) <); config = (> ._def_setcmd_opt("c?lear", 'symbol, opts, config) <); config = (> ._def_setcmd_opt("f?ormat", 'symbol, opts, config) <); config = (> ._def_setcmd_opt("a?ccess", 'symbol, opts, config) <); if (type == 'itemlist) { config = config.add('parse, ['parse_itemlist, @(| config['parse] |) || []]); if (!(config.contains('format))) config = config.add('format, ['format_itemlist]); } // now reconfig it for o in (config) { catch any (> definer.set_setting_attr(name, @o) <); with .tell((traceback()[1])[2]); } .tell(((("Reconfigured setting " + definer) + ":") + name) + " as:"); config = (definer.defined_settings())[name]; for o in (config) { val = o[2]; o = o[1]; .tell(((" +" + strsub(tostr(o), "_", "-")) + "=") + (val[1])); if (listlen(val) > 1) .tell(((" +" + strsub(tostr(o), "_", "-")) + "-args=") + (map m in (sublist(val, 2)) to (toliteral(m)).join(","))); } }; protected method .create_cmd() { arg cmdstr, cmd, args; var new, parents, obj; (> .perms(caller(), 'command) <); args = (args.replace(" from ", " ")).explode(); if ((!args) || ((args.length()) < 2)) .tell_error(cmd + " <object> [from] <parent>[, <parent> ...]"); new = args[1]; parents = []; for obj in (args.subrange(2)) parents += [.match_env_nice(obj)]; }; public method .define_setting_cmd() { arg cmdstr, cmd, args; var def_opts, opts, o, name, config, m, definer, syn, type, def, val; syn = ("Syntax: `" + cmd) + " <definer>:<setting>[=default] [options]`"; def_opts = []; for o in (["get", "set", "parse"]) def_opts += [[o, 1], [o + "-a?rgs", 1]]; def_opts += [["c?lear", 1], ["f?ormat", 1], ["a?ccess", 1], ["t?ype", 1]]; [args, opts] = $parse_lib.getopt(args, def_opts); args = join(args, " "); if (!args) { .tell(syn); for o in (def_opts.slice(1)) .tell((((" +" + o) + "=<") + ((o.strip("?")).replace("-", " "))) + ">"); return "Types can be any ColdC type and \"boolean\""; } if ((m = regexp(args, "^([^=]+) *= *(.*)$"))) [args, def] = m; else def = ""; if ((m = regexp(args, "^([^:]+) *: *([\@a-z0-9-]+)"))) [definer, name] = m; else return syn; catch any definer = (> .match_environment(definer) <); with return (traceback()[1])[2]; if (!name) return syn; // setup some default config opts based off the desired type config = #[]; if ((m = "t?ype" in (opts.slice(1)))) { type = (| ((opts[m])[4]).to_symbol() |); if ((!type) || (!($settings.is_valid_type(type)))) return "Types can be any ColdC type and " + (($settings.valid_types()).to_english("", " or ")); switch (type) { case 'boolean: config = #[['parse, ['is_boolean]], ['format, ['format_boolean]]]; case 'itemlist: // do nothing, we re-adjust things later default: config = #[['parse, ['is_type, type]]]; } } // now build default config--not the most efficient way--but cleaner config = (> ._def_setcmd_opt("get", 'symbol, opts, config) <); config = (> ._def_setcmd_opt("get-a?rgs", 'data_list, opts, config) <); config = (> ._def_setcmd_opt("set", 'symbol, opts, config) <); config = (> ._def_setcmd_opt("set-a?rgs", 'data_list, opts, config) <); config = (> ._def_setcmd_opt("parse", 'symbol, opts, config) <); config = (> ._def_setcmd_opt("parse-a?rgs", 'data_list, opts, config) <); config = (> ._def_setcmd_opt("c?lear", 'symbol, opts, config) <); config = (> ._def_setcmd_opt("f?ormat", 'symbol, opts, config) <); config = (> ._def_setcmd_opt("a?ccess", 'symbol, opts, config) <); if (type == 'itemlist) { config = config.add('parse, ['parse_itemlist, @(| config['parse] |) || []]); if (!(config.contains('format))) config = config.add('format, ['format_itemlist]); } // now add it.. catch any config = (> definer.define_setting(name, config) <); with return (traceback()[1])[2]; .tell(((("-- Defined setting " + definer) + ":") + name) + " as:"); for o in (config) { val = o[2]; o = o[1]; .tell(((" +" + strsub(tostr(o), "_", "-")) + "=") + (val[1])); if (listlen(val) > 1) .tell(((" +" + strsub(tostr(o), "_", "-")) + "-args=") + (sublist(val, 2).join(","))); } // and set the default value catch any { o = definer; o = o.set_setting(name, o, def); val = o.format_setting(name, o, o.get_setting(name, o)); return ["-- Default Setting:", ((" " + name) + " = ") + val, "--"]; } with { return (traceback()[1])[2]; } }; protected method .del_command_cmd() { arg cmdstr, cmd, args; var ref, t, objref; (> .perms(caller(), 'command) <); args = args.explode_quoted(); if (listlen(args) > 2) { if ((args[2]) == "from") args = delete(args, 2); t = delete(args, listlen(args)).join(); objref = args.last(); } else if (listlen(args) == 2) { t = args[1]; objref = args[2]; } else { return ("Syntax: `" + cmd) + " \"template\" [from] <objref>"; } catch any { ref = (> $parse_lib.ref(objref) <); if ((ref[1]) != 'method) return ("The reference " + objref) + " is not for a method."; if ((!(ref[4])) || (!((ref[4]).valid_ident()))) return ((("Invalid method name " + (ref[3])) + ".") + (ref[4])) + "()."; if (!(> (ref[2]).del_command(t, tosym(ref[4])) <)) return strfmt("Command %d is not defined on %s.", t, ref[2]); } with { return (traceback()[1])[2]; } return strfmt("Command %d removed from %s.%s()", t, ref[3], ref[4]); }; protected method .del_method_cmd() { arg cmdstr, cmd, objref; var name, obj; (> .perms(caller(), 'command) <); if (!(objref[4])) return .tell(("No method specified to delete from " + (objref[3])) + "."); if (!(| (name = tosym(objref[4])) |)) return .tell(("Invalid method name \"" + (objref[4])) + "\"."); catch any { (> (objref[3]).del_method(name) <); .tell(((("Method " + (objref[3])) + ".") + name) + "() deleted."); } with { if (error() == ~methodnf) .tell(((("Method " + (objref[3])) + ".") + name) + "() does not exist."); else .tell((traceback()[1])[2]); } }; protected method .del_parent_cmd() { arg cmdstr, cmd, args; var syn, obj, parent; (> .perms(caller(), 'command) <); args = args.explode(); if ((listlen(args) > 2) && ((args[2]) == "from")) args = delete(args, 2); if (listlen(args) != 2) return ("Syntax: `" + cmd) + " <parent> [from] <object>`"; if (!args) .tell_error(syn); parent = (> .match_env_nice(args[1]) <); obj = (> .match_env_nice(args[2]) <); catch any { (> obj.del_parent(parent) <); return ((("Deleted parent from " + (obj.namef('ref))) + ", parents: ") + ((obj.parents()).to_english())) + "."; } with { return (traceback()[1])[2]; } }; protected method .del_shortcut_cmd() { arg cmdstr, cmd, args; var ref, syn; (> .perms(caller(), 'command) <); args = args.explode_quoted(); if ((listlen(args) == 3) && ((args[2]) == "from")) args = delete(args, 2); if (listlen(args) != 2) return ("Syntax: `" + cmd) + " \"<shortcut>\" [from] <objref>"; ref = (> $parse_lib.ref(args[2]) <); if (((ref[1]) != 'method) || ((!(ref[4])) || (!(| tosym(ref[4]) |)))) return ("Invalid method reference reference \"" + (args[3])) + "\"."; catch any (> (ref[2]).del_shortcut(args[1]) <); with return (traceback()[1])[2]; return strfmt("Deleted shortcut %d from %s.%s().", args[1], ref[2], ref[4]); }; protected method .del_var_cmd() { arg cmdstr, cmd, ref; (> .perms(caller(), 'command) <); if (((ref[1]) != 'variable) || (!(ref[4]))) return "Invalid obj,variable reference."; catch ~symbol (ref[3]).del_var(tosym(ref[4])); with return (traceback()[1])[2]; return ((("Object variable " + (ref[3])) + ",") + (ref[4])) + " deleted."; }; protected method .descendants_cmd() { arg cmdstr, cmd, args; var obj, max, line, opts, i, r, not, only, f; (> .perms(caller(), 'command) <); // parse args [obj, args, opts] = args; if ((obj[1]) != 'object) return "Object reference must simply be a dbref."; if ((obj[2]) != (obj[3])) .tell(("Ignoring specified definer " + (obj[2])) + "."); obj = obj[3]; only = (not = []); max = 1; if ((i = "o?nly" in (opts.slice(1)))) { only = map f in (((opts[i])[4]).split(" *, *")) to (f.to_symbol()); opts = delete(opts, i); } if ((i = "n?ot" in (opts.slice(1)))) { not = map f in (((opts[i])[4]).split(" *, *")) to (f.to_symbol()); opts = delete(opts, i); } if ((i = "r?edundant" in (opts.slice(1)))) { r = (opts[i])[3]; opts = delete(opts, i); } if ((i = "a?ll" in (opts.slice(1)))) { if ((opts[i])[3]) max = 0; opts = delete(opts, i); } if (opts) { if ((max = find i in (opts) where ((i[2]).is_numeric()))) { max = toint((opts[max])[2]); if (max < 1) return "Maximum levels must be greater than zero."; } } // do it line = ("-- Descendants of " + obj) + " ["; .tell((line + (((obj.parents()).mmap('objname)).to_english())) + "]"); if (max) { line = (max + " level") + ((max > 1) ? "s" : ""); max++; } else { line = "all levels"; } if (r) line += ", redundant entries"; .tell("-- " + line); if (only) .tell("-- only objects with flag(s): +" + (only.join(" +"))); if (not) .tell("-- not objects with flag(s): +" + (not.join(" +"))); return [obj.format_descendants("", #[], 0, max, only, not, r), "--"]; }; protected method .display_cmd() { arg cmdstr, cmd, args; var opts, slice, what, match, i, chop, f, gen, def, obj, out; (> .perms(caller(), 'command) <); opts = args[3]; args = args[1]; chop = 1; slice = opts.slice(1); if ((i = "c?hop" in slice) && (!((opts[i])[3]))) chop = 0; else chop = .linelen(); def = args[3]; if ((i = "g?enerations" in slice)) { gen = (opts[i])[4]; if (gen.is_numeric()) gen = ['generations, toint(gen)]; else if (gen) gen = ['ancestors_descending, (> .match_env_nice(gen) <)]; else gen = ['ancestors_to, def]; def = 0; } else { gen = ['generations, 1]; } what = [args[1]] + ((| args[5] |) ? [args[5]] : []); obj = args[2]; if (type(obj) == 'frob) return ["The target object was a frob. Please use @exam instead."]; out = $object_lib.format_object(obj, chop); if (!(args[4])) f = .get_setting("match-default", $programmer); else f = args[4]; match = .get_setting("match-with", $programmer); if ('method in what) out += ._display_methods(obj, obj.list_methods(gen, def, f, match), chop, f); if ('variable in what) out += ._display_variables(obj, obj.variable_info(gen, def, f, match), chop, f); return out + ["---"]; }; protected method .dump_cmd() { arg cmdstr, cmd, args; var opts, objs, o, i, tdfmt, meths, vars, header; (> .perms(caller(), 'command) <); opts = args[2]; args = args[1]; o = opts.slice(1); (i = "t?extdump" in o) && (tdfmt = (opts[i])[3]); (i = "m?ethods" in o) ? (meths = (opts[i])[3]) : (meths = 1); (i = "v?ariables" in o) ? (vars = (opts[i])[3]) : (vars = 1); (i = "h?eader" in o) ? (header = (opts[i])[3]) : (header = 1); if ((!meths) && (!vars)) return "Perhaps you will want to dump methods and/or vars next time?"; objs = []; for o in (args) { catch any objs += [(> .match_env_nice(o) <)]; with .tell((traceback()[1])[2]); } if (!objs) return "Dump nothing?"; if (tdfmt) .dump_fmt_textdump(objs, meths, vars, header); else .dump_fmt_commands(objs, meths, vars, header); }; protected method .dump_fmt_commands() { arg objs, meths, vars, header; var data, obj, out, a, v, m, line, pars, cmdopts; // this uses .tell() to keep its internal overhead from bloating // it could be faster by building a list and printing it all at once // but this is nicer on the server (especially when dumping large objects). for obj in (objs) { refresh(); if (header) { pars = obj.parents(); line = (((((((";var p, new; if(!(| valid(" + obj) + ") |)) ") + "{ new = ") + (pars[1])) + ".spawn();") + " new.set_objname('") + (obj.objname())) + ");}"; if (listlen(pars) > 1) line += (" obj.chparents(" + join(pars, ",")) + ");"; .tell(line); } if (vars) { catch ~perm { data = (> obj.data() <); for a in (dict_keys(data)) { refresh(); for v in (data[a]) { if (a == obj) .tell(strfmt("@av %l,%l = %d", obj, @v)); .tell(strfmt(";|as %l<%l>;%l = %d;", obj, a, @v)); } } } with { .tell((traceback()[1])[2]); } } if (meths) { cmdopts = .get_setting("@program-options", $programmer); catch ~perm { for m in ((> obj.methods() <)) { refresh(); .tell(.format_method(obj, m, 'normal, cmdopts)); } } with { .tell((traceback()[1])[2]); } } } }; protected method .dump_fmt_textdump() { arg objs, meths, vars, header; var data, obj, out, a, v, m; // this uses .tell() to keep its internal overhead from bloating // it could be faster by building a list and printing it all at once // but this is nicer on the server (especially when dumping large objects). for obj in (objs) { refresh(); if (header) .tell([((("object " + obj) + ": ") + ((obj.parents()).join(", "))) + ";", ""]); if (vars) { catch ~perm { data = (> obj.data() <); for a in (dict_keys(data)) { refresh(); for v in (data[a]) .tell(strfmt("var %l %l = %d;", a, @v)); } } with { .tell((traceback()[1])[2]); } } .tell(""); if (meths) { catch ~perm { for m in ((> obj.methods() <)) { refresh(); .tell([""] + (.format_method(obj, m, 'textdump))); } } with { .tell((traceback()[1])[2]); } } } }; protected method .eval_cmd() { arg cmdstr, com, str; var result, adjust, vars, v, evalp, times, line, reg, obj, definer, ref, debug; (> .perms(caller(), 'command) <); evalp = .eval_prefix(); vars = (evalp.keys()).join(", "); v = (evalp.values()).join(); // clean it up str = strsed(str, "^;*", ""); // perform escape substitution if (str && ((str[1]) == "|")) str = substr(str, 2); else str = .eval_subs(str); // check for debug flags if ((reg = regexp(str, "^(trace|debug|profile) *;*(.*)$"))) { [debug, str] = reg; debug = #[["trace", 'trace], ["debug", 'debug], ["profile", 'profile]][debug]; } else { debug = 0; } // who are we evaluating as if ((reg = regexp(str, "^ *as +([^; ]+)"))) { ref = $parse_lib.ref(reg[1]); obj = ref[2]; definer = ref[3]; str = strsed(str, "^ *as +([^; ]+)[ ;]+", ""); if ((!(definer.is_writable_by(this()))) || (!(obj.is_writable_by(this())))) return ("You do not have permission to evaluate on " + (reg[1])) + "."; if (!(obj.is(definer))) return (obj + " isn't a child of ") + definer; } else { obj = (definer = this()); } // are we just adjusting our offset? if (!str) { result = (> .evaluate(((("var " + vars) + ";") + v) + "return (> 1 <);", obj, definer, 'no_offset) <); result = replace(result[1], 1, ((result[1])[1]) - 1); if (eval_offset) line = strfmt("adjusted by %s ticks and %s.%6{0}r seconds.", (eval_offset[1]) - (result[1]), (eval_offset[2]) - (result[2]), abs((eval_offset[3]) - (result[3]))); else line = strfmt("set to %s ticks and %s.%6{0}r seconds.", @result); eval_offset = result; return "Eval offset " + line; } // format it, use heuristics if (match_begin(str, "var") && (reg = regexp(str, "var ([^;]+)"))) { str = strsed(str, "var ([^;]+);", ""); str = ((((("var " + vars) + ", ") + (reg.join(","))) + ";") + v) + str; } else if ("return" in str) { str = ((("var " + vars) + ";") + v) + str; } else { str = strsed(str, " *;* *$", ""); str = ((((("var " + vars) + ";") + v) + "return (> ") + str) + " <);"; } if (debug) [times, result, debug] = (> .evaluate(str, obj, definer, debug) <); else [times, result] = (> .evaluate(str, obj, definer) <); // Display the errors, or the result. if ((result[1]) == 'errors) { .tell(result[2]); } else if ((result[1]) == 'traceback) { .tell_traceback(result[2]); line = strfmt("[ seconds: %l.%6{0}r; operations: %s", times[2], times[3], times[1]); if (times[2]) line += (" (" + ((times[1]) / (times[2]))) + " ticks per second)"; return line + " ]"; } else { if (type(result[2]) == 'objnum) .tell("=> " + ((| (result[2]).namef('xref) |) || (result[2]))); else .tell("=> " + toliteral(result[2])); if (debug) .tell(debug); line = strfmt("[ seconds: %l.%6{0}r; operations: %s", times[2], times[3], times[1]); if (times[2]) line += (" (" + ((times[1]) / (times[2]))) + " ticks per second)"; return line + " ]"; } }; protected method .eval_offset() { return eval_offset || #[['mtime, 0], ['time, 0], ['ticks, 0]]; }; public method .eval_prefix() { return #[["me", ("me = " + this()) + ";"], ["here", ("here = " + (.location())) + ";"]].union(eval_prefix || #[]); }; protected method .eval_subs() { arg code; var idx, ret_code, sub; ret_code = ""; while (code) { idx = "^" in code; if (!idx) { return ret_code + code; } else if ((idx == (code.length())) || ((code.subrange(idx + 1, 1)) == "^")) { ret_code += code.subrange(1, idx); code = code.subrange(idx + 1); if (code && ((code[1]) == "^")) code = code.subrange(2); } else { if (idx > 1) { ret_code += code.subrange(1, idx - 1); code = code.subrange(idx + 1); } else { code = code.subrange(2); } idx = 1; while ((idx <= (code.length())) && (!((code[idx]) in " =.()[]=<>?|&!*+-/';\""))) idx++; sub = .match_env_nice(code.subrange(1, idx - 1)); ret_code += sub; code = code.subrange(idx); } } return ret_code; }; public method .evaluate() { arg str, obj, definer, @mode; var start, end, time, ticks, mtime, times1, times2, method, errs, trace, result, is_error; mode = mode ? (mode[1]) : 0; if (sender() != $eval_parser) (> .perms(caller(), $programmer) <); method = tosym("tmp_eval_" + time()); if ((errs = (> definer.add_method([str], method, 'evalonly) <))) { if (mode) return [[0, 0, 0], ['errors, errs, 0, 0], []]; else return [[0, 0, 0], ['errors, errs, 0, 0]]; } catch any { if (mode in ['trace, 'profile]) debug_callers(1); else if (mode == 'debug) debug_callers(2); times1 = [tick(), time(), mtime()]; result = (> obj.(method)() <); times2 = [mtime(), time(), tick()]; trace = call_trace(); debug_callers(0); } with { times2 = [mtime(), time(), tick()]; result = traceback(); is_error = 1; debug_callers(0); } (| definer.del_method(method) |); // figure up the actual times time = (times2[2]) - (times1[2]); ticks = (times2[3]) - (times1[1]); if ((times2[1]) > (times1[3])) mtime = (times2[1]) - (times1[3]); else if (time) mtime = ((time * 1000000) + (1000000 - (times1[3]))) + (times2[1]); else mtime = (1000000 - (times2[1])) + (times1[3]); // offset it? if (eval_offset && (mode != 'no_offset)) { ticks -= eval_offset[1]; time -= eval_offset[2]; mtime -= eval_offset[3]; } if (trace) return [[ticks, time, abs(mtime)], ['result, result], $code_lib.generate_debug_listing(trace, mode)]; return [[ticks, time, abs(mtime)], [is_error ? 'traceback : 'result, result]]; }; protected method .format_method() { arg obj, method, format, @opts; var code, opt, flags, f; // this needs to be on $programmer ot get the programmers perms [(opt ?= "")] = opts; code = obj.list_method(method); flags = obj.method_flags(method); switch (format) { case 'textdump: return ([(((((((obj.method_access(method)) + " method ") + obj) + ".") + method) + "()") + (flags ? (": " + (flags.join(", "))) : "")) + " {"] + (code.prefix(" "))) + ["};"]; case 'numbered: code = code.numbered_text(); return ([(((((("-- " + (obj.method_access(method))) + " method ") + obj) + ".") + method) + "()") + (flags ? (": " + (flags.join(", "))) : "")] + code) + ["--"]; default: return ([$object_lib.format_method_header(obj, method, opt, flags, obj.method_access(method))] + (code.prefix(" "))) + ["."]; } }; protected method .grep_brief() { arg regexp, objs; var obj, method, out, x, l, line, lines, code; for obj in (objs) { if (!valid(obj)) continue; if (!(obj.has_flag('code))) { .tell(("You cannot read method code on " + obj) + ", skipping.."); continue; } out = []; for method in (obj.methods()) { code = obj.list_method(method); lines = []; for x in [1 .. listlen(code)] { l = code[x]; if (match_regexp(l, regexp)) lines += [x]; refresh(); } if (lines) out += [(((obj + ".") + method) + "(): ") + (lines.to_english())]; refresh(); } if (out) .tell(out); refresh(); } return "---"; }; protected method .grep_cmd() { arg cmdstr, cmd, args; var more, regexp, from, syn, opts, d, f, l, r, rep, slice, objs, obj, out; (> .perms(caller(), 'command) <); [more, opts] = args; if ((more.length()) < 2) return ("=> Syntax: `" + cmd) + " [options] <regexp> <object> <object>.."; regexp = more[1]; more = more.subrange(2); // handle the options slice = opts.slice(1); if ((r = (| "r?eplace-with" in slice |))) { rep = (opts[r])[4]; r = (opts[r])[3]; } if ((d = (| "d?escend" in ((args[2]).slice(1)) |))) d = (opts[d])[3]; if ((l = (| "l?ist" in ((args[2]).slice(1)) |))) l = (opts[l])[3]; if ((f = (| "f?ull" in ((args[2]).slice(1)) |))) f = (opts[f])[3]; // now we check for conflicting or incorrect options.. if (d && (!(.is($admin)))) return "Only administrators may use the +descend option, talk to one."; if (d && ((more.length()) > 1)) return "+descend can only be used with a single object as the target."; if (r && (f || l)) return "+replace-with option cannot be used with +full or +list."; if (f && l) return "+full cannot be used with +list."; // the pause() flushes so we can see the 'Searching for ..' // Do this now because .descendants() can lag .tell(("Searching for \"" + regexp) + "\"..."); pause(); // figure out our targets if (d) { obj = (> .match_env_nice(more[1]) <); objs = [obj, @obj.descendants()]; } else { objs = []; for obj in (more) objs += [(> .match_env_nice(obj) <)]; } // call the right grep method if (r) return (> .grep_replace(regexp, objs, rep) <); else if (l) return (> .grep_list(regexp, objs) <); else if (f) return (> .grep_full(regexp, objs) <); else return (> .grep_brief(regexp, objs) <); }; protected method .grep_full() { arg regexp, objs; var obj, method, out, x, l, code; for obj in (objs) { if (!valid(obj)) continue; if (!(obj.has_flag('code))) { .tell(("You cannot read method code on " + obj) + ", skipping.."); continue; } out = []; for method in (obj.methods()) { code = obj.list_method(method); for x in [1 .. listlen(code)] { l = code[x]; if (match_regexp(l, regexp)) out += [(((((obj + ".") + method) + "() line ") + x) + ": ") + l]; refresh(); } refresh(); } if (out) .tell(out); refresh(); } return "---"; }; protected method .grep_list() { arg regexp, objs; var obj, code, x, l, lr, what, loop, method, opt; opt = .get_setting("@program-options", $programmer); for obj in (objs) { if (!valid(obj)) continue; if (!(obj.has_flag('code))) { .tell(("You cannot read method code on " + obj) + ", skipping.."); continue; } for method in (obj.methods()) { code = obj.list_method(method); for l in (code) { if (match_regexp(l, regexp)) { .tell(([$object_lib.format_method_header(obj, method, opt, obj.method_flags(method), obj.method_access(method))] + (code.prefix(" "))) + ["."]); break; } refresh(); } refresh(); } refresh(); } return "---"; }; protected method .grep_replace() { arg regexp, objs, replace; var obj, method; for obj in (objs) { if (!valid(obj)) continue; if (!(obj.is_writable_by(this()))) { .tell(("You cannot write on " + obj) + ", skipping.."); continue; } for method in (obj.methods()) { (> .grep_replace_method(obj, method, regexp, replace) <); refresh(); } refresh(); } return "Done."; }; protected method .grep_replace_method() { arg obj, method, regexp, replace; var old_code, code, x, l, lr, errs, what; old_code = (code = obj.list_method(method)); for x in [1 .. listlen(code)] { l = code[x]; if (!match_regexp(l, regexp)) continue; lr = strsed(l, regexp, replace, "g"); .tell([((((("Change " + obj) + ".") + method) + "() line ") + x) + " from:", " " + l, "to:", " " + lr]); what = .prompt("? (yes, no, abort, abort-all) [yes] "); if ((!what) || (what in ["yes", "y"])) { code = replace(code, x, lr); } else if (what == "abort") { .tell("Aborting method .."); return; } else if (what == "abort-all") { throw(~stop, "Aborting grep replace"); } else if (!(what in ["no", "n"])) { .tell(("Unknown command '" + what) + "', assuming 'no'."); } refresh(); } if ((old_code != code) && (errs = obj.add_method(code, method))) .tell(((([((("Error in compilation of updated method " + obj) + ".") + method) + "():"] + (errs.prefix(" "))) + ["-- Method code: "]) + (code.prefix(" "))) + ["--"]); }; protected method .help_list_cmd() { arg cmdstr, cmd, str; var node, out; (> .perms(caller(), 'command) <); if (!str) node = .current_node(); else node = .parse_help_reference(str); if (!(node.is($help_node))) return (node.namef('ref)) + " is not a descendant of $help_node."; return (["@hwrite " + node] + ((node.body()).uncompile())) + ["."]; }; protected method .help_write_cmd() { arg cmdstr, cmd, str; var node, text, errors, ignore; (> .perms(caller(), 'command) <); if (!str) { node = .current_node(); } else { catch any { node = .parse_help_reference(str); } with { .tell(("-- " + ((traceback()[1])[2])) + " --"); .read("-- Ignoring until '.' or @abort --"); return "Done ignoring."; } } if (!(node.is($help_node))) { ignore++; .tell((node.namef('ref)) + " is not a descendant of $help_node, ignoring."); } if (!(node.is_writable_by(this()))) { ignore++; .tell(("You cannot write help on " + (node.name())) + ", ignoring."); } text = .read(("-- Enter CML text for " + (node.namef('ref))) + " --"); if (text == 'aborted) return; if (ignore) return "Done ignoring."; node.set_body(text); return ("New help text set for " + (node.namef('ref))) + "."; }; protected method .id_cmd() { arg cmdstr, cmd, obj; (> .perms(caller(), 'command) <); obj = .match_env_nice(obj); if (type(obj) == 'frob) return ["The target object was a frob."]; .tell((((((((obj.namef('xref)) + " ") + ($object_lib.see_perms(obj))) + " ") + toliteral(obj.parents())) + " ") + tostr(obj.size())) + " bytes"); }; protected method .join_cmd() { arg cmdstr, cmd, who; var loc, p, user; (> .perms(caller(), 'command) <); if (!who) { .tell("Specify a user to join."); return; } catch any { if ((who[1]) in "$#") { user = (> $object_lib.to_dbref(who) <); if (!(user.has_ancestor($thing))) return "You can only join things in the VR."; } else { user = (> $user_db.search(who) <); } } with { .tell((traceback()[1])[2]); return; } loc = user.location(); if (loc == (.location())) { .tell(("You are already with " + (user.name())) + "!"); return; } if (!(.teleport(loc))) .tell("Sorry."); else .tell(("You join " + (user.name())) + "."); }; protected method .list_cmd() { arg cmdstr, cmd, args; var i, pattern, ref, methods, s, def, method, opts, str, m, d, out, type; (> .perms(caller(), 'command) <); if ((opts = .get_setting("@list-options", $programmer))) { opts = $parse_lib.opt(opts, "n?umbers", "t?extdump"); opts = union(args[3], opts[2]); } else { opts = args[3]; } if ((i = (| "n?umbers" in (opts.slice(1)) |)) && ((opts[i])[3])) type = 'numbered; else if ((i = (| "t?extdump" in (opts.slice(1)) |)) && ((opts[i])[3])) type = 'textdump; else type = 'normal; ref = args[1]; if ((ref[1]) == 'variable) return ((("The reference " + (ref[3])) + ",") + ((ref[4]) || "")) + " is not for a method."; if ((ref[1]) == 'object) return ("The reference " + (ref[3])) + " is not for a method."; def = (| (ref[2]).find_method(tosym(ref[4])) |); if (def) { pattern = ref[4]; methods = [tosym(ref[4])]; } else { if (ref[4]) pattern = ref[4]; else pattern = .get_setting("match-default", $programmer); def = ref[3]; m = .get_setting("match-with", $programmer); methods = []; for method in (def.methods()) { if (tostr(method).(m)(pattern)) methods += [method]; } if (!methods) return .tell((("No method found matching " + def) + ".") + pattern); } cmd = .get_setting("@program-options", $programmer); out = []; for method in (methods) out += .format_method(def, method, type, cmd); return out; }; protected method .local_edit_cmd() { arg cmdstr, cmd, args; var ref, edited, code, def, meth, i; (> .perms(caller(), 'command) <); ref = args[1]; if ((ref[1]) == 'variable) return ((("The reference " + (ref[3])) + ",") + ((ref[4]) || "")) + " is not for a method."; if ((ref[1]) == 'object) return ("The reference " + (ref[3])) + " is not for a method."; if ((ref[3]) && (!((ref[3]).is_writable_by(this())))) return "You cannot program on that object."; if ((!(ref[4])) || (!((ref[4]).valid_ident()))) return ("The method name '" + (ref[4])) + "' is not acceptable."; meth = tosym(ref[4]); catch ~methodnf { def = (ref[3]).find_method(meth); if (!(def.is_writable_by(this()))) return ("You cannot program on " + def) + "."; } with { return ((("Method " + (ref[3])) + ".") + meth) + "() not found."; } if ((i = "e?dited" in ((args[3]).slice(1)))) { if (!(((args[3])[i])[3])) { if (!($sys.is_admin(this()))) return "Only admins can shut off edited comments."; } else { edited = 1; } } else { edited = 1; } if (edited) { edited = (("// $#Edited: " + ($time.format("%d %h %y %H:%M"))) + " ") + this(); if (i && (((args[3])[i])[4])) edited += ": " + (((args[3])[i])[4]); } catch ~perm code = def.list_method(meth); with return (traceback()[1])[2]; return ([(((((("#$# edit name: " + def) + ".") + meth) + " upload: @program ") + def) + ".") + meth] + (code.prefix(" "))) + ["."]; }; protected method .managed_cmd() { arg cmdstr, cmd, args; var manager, managed, obj, out, len; (> .perms(caller(), 'command) <); manager = (| .match_environment(args) |); if (!manager) { manager = (| $user_db.search(args) |); if (!manager) return ("Unable to find \"" + args) + "\"."; } managed = manager.managed(); if (!managed) return (manager.namef('ref)) + " does not manage any objects."; out = [(manager.namef('ref)) + " manages:"]; len = (.linelen()) / 2; for obj in (managed) { if (!valid(obj)) { .tell((" ** invalid object (" + obj) + ") **"); continue; } out += [((" " + ((obj.namef('xref)).pad(len))) + " ") + ($object_lib.see_perms(obj, ["", ""]))]; } return out + ["---"]; }; protected method .move_cmd() { arg cmdstr, cmd, args; var src, dest, comment, i, how; (> .perms(caller(), 'command) <); // is this actually @copy|@cp? how = match_begin(cmd, "@c") ? 'copy : 'move; // drop back to $builder.move_cmd if it is just an object if (((args[1])[1]) == 'object) { if (how == 'copy) return "You cannot copy objects!"; return (> pass(cmdstr, cmd, [(args[1])[2], args[2], args[3]]) <); } // options if ((i = "c?omment" in ((args[3]).slice(1)))) comment = (((args[3])[i])[4]) || (((args[3])[i])[3]); else comment = 1; // move|copy a method or var src = args[1]; args = args[2]; if ((args[1]) == "to") args = delete(args, 1); if (!args) return ((("You have to " + how) + " ") + (what.namef('ref))) + " somewhere."; catch ~objnf dest = (> $parse_lib.ref(args.join()) <); with return (traceback()[1])[2]; if ((dest[1]) == 'object) dest = [src[1], dest[2], dest[3], src[4], 0]; if ((src[1]) != (dest[1])) return ((((("You cannot " + how) + " a ") + (src[1])) + " to a ") + (dest[1])) + "."; if (!(src[4])) return ("Invalid " + (src[1])) + " reference, no name specified."; if (!(dest[4])) dest = replace(dest, 4, src[4]); if (((src[3]) == (dest[3])) && ((src[4]) == (dest[4]))) return ((("Why do you want to " + how) + " the ") + (src[1])) + " to itself?"; catch ~symbol { src = replace(src, 4, (> tosym(src[4]) <)); dest = replace(dest, 4, (> tosym(dest[4]) <)); } with { return ("You cannot specify wildcards in the " + (src[1])) + " name."; } if ((how == 'move) && (!((src[3]).is_writable_by(this())))) return ("You do not have permission to move from " + (src[3])) + "."; if (!((dest[3]).is_writable_by(this()))) return ((("You do not have permission to " + how) + " to ") + (dest[3])) + "."; catch any (> .(tosym("_move_" + (src[1])))(how == 'move, src[3], src[4], dest[3], dest[4], comment) <); with return (traceback()[1])[2]; return ((((("You " + how) + " ") + ($parse_lib.buildref(@src))) + " to ") + ($parse_lib.buildref(@dest))) + "."; }; protected method .new_editor_session() { arg ref, opts, type; var def, code, name; switch (ref[1]) { case 'variable: (> .tell_error("", "Variable editor not yet implemented.") <); case 'method: def = (| (ref[2]).find_method(tosym(ref[4])) |); if (!def) { def = ref[3]; code = []; } else { code = def.list_method(tosym(ref[4])); } (> .invoke_editor(this(), '_edit_method_callback, code, [def, tosym(ref[4])]) <); default: return (> pass(ref, opts, type) <); } if (.active_editor()) return [("Editing " + ((.active_editor()).session_name())) + ".", "Type 'help' to list available commands."]; else return ["Remote editing invoked."]; }; protected method .new_help_node_cmd() { arg cmdstr, cmd, args; var new, name, p, parent, i, syn, m, index, opts, objname, o; (> .perms(caller(), 'command) <); syn = ("=> Syntax: `" + cmd) + " [<parent node>] [options]`"; [args, opts] = args; o = opts.slice(1); if ((i = "n?amed" in o)) { name = (opts[i])[4]; if (!name) return [syn, "Option +n?amed requires a followup argument."]; } if ((i = "o?bjname" in o)) { objname = (| ((opts[i])[4]).to_symbol() |); if (!objname) return [syn, "Option +o?bjname requires a followup argument."]; } // now use 'i' as the string rep of 'index' if ((i = "i?ndex" in o)) { i = (opts[i])[4]; if (!i) return [syn, "Option +i?ndex requires a followup argument."]; } // now figure out the parent node if (args) { p = args[1]; catch any parent = (> .parse_help_reference(p) <); with return (traceback()[1])[2]; } else { parent = .current_node(); } // figure out the index if (i) { if ((i[1]) in ["$", "#"]) index = (| $object_lib.to_dbref(i) |); else index = $help_index.match_children(i); if (!index) return [syn, ("! Unable to find index '" + i) + "'"]; if (!(index.has_ancestor($help_index))) return [syn, ("! '" + (index.namef('ref))) + "' is not a help index."]; } // create it new = (> parent.spawn() <); .tell(((("Created new node " + new) + " from ") + (parent.namef('ref))) + "."); // change its objname? if (objname) { catch any { (> new.set_objname(objname) <); .tell(("Changed node's objname to " + new) + "."); } with { .tell("set_objname(): " + ((traceback()[1])[2])); } } // set its name? if (name) { catch any { (> new.set_name(name) <); .tell(("Changed node's name to " + (new.name())) + "."); } with { .tell("set_name(): " + ((traceback()[1])[2])); } } else { .tell(("No name specified, set it with `@rename " + new) + " to <name>`"); } // set its index? if (index) { catch any { (> new.set_index(index) <); .tell(("Set node's index to " + (index.namef('ref))) + "."); } with { .tell("set_index(): " + ((traceback()[1])[2])); } } else { .tell(("No index specified, set it with `@set " + new) + ":index=<index>`"); } }; public method .parse_match_with() { arg value, @args; if (value in ["regexp", "pattern", "begin"]) return tosym("match_" + (value.lowercase())); throw(~perm, "You can match with: regexp, pattern, begin."); }; public method .parse_methodcmd_options() { arg syntax, args, @more; var o, opt, opts, out, r, l; o = ([@more, []][1]) + [["pub?lic"], ["r?oot"], ["dr?iver"], ["pri?vate"], ["pro?tected"], ["no?override"], ["s?yncronized"], ["l?ocked"], ["na?tive"]]; opts = #[['exists, 0], ['ignore, 0], ['mflags, []], ['mstate, 'public], ['error, 0]].union([@more, #[], #[]][2]); args = $parse_lib.getopt(args, o); if (!(args[1])) { out = []; for opt in (o) out += [" +|-" + (opt[1])]; (> .tell_error(syntax, ["Valid options:"] + (out.lcolumnize())) <); } r = (| $parse_lib.ref((args[1]).join()) |); if (!r) { opts = opts.add('error, "Invalid <object>.<method> reference."); opts = opts.add('ignore, 1); } if (!((r[4]).valid_ident())) { opts = opts.add('error, (((r[2]) + ".") + tostr(r[4])) + "() is not a valid method reference."); opts = opts.add('ignore, 1); } r = replace(r, 4, tosym(r[4])); if ((r[2]) && (!((r[2]).is_writable_by(this())))) { opts = opts.add('error, ("You cannot program " + (r[2])) + "."); opts = opts.add('ignore, 1); } if ((| (r[2]).find_method(r[4]) |) == (r[2])) { opts = opts.add('mflags, (r[2]).method_flags(r[4])); opts = opts.add('mstate, (r[2]).method_access(r[4])); opts = opts.add('exists, 1); } opts = opts.add('object, r[2]); opts = opts.add('method, r[4]); for opt in (args[2]) { switch (opt[1]) { case "pub?lic", "r?oot", "dr?iver", "pri?vate", "pro?tected": opts = opts.add('mstate, (opt[1]).to_symbol()); case "no?override", "s?yncronized": opts = opts.add('mflags, (opts['mflags]).setadd((opt[1]).to_symbol())); case "l?ocked": .tell("You cannot set the locked flag on a method."); case "n?ative": .tell("You cannot set the native flag on a method."); default: if (!(opt[1])) { .tell(("Unknown option: \"" + (opt[2])) + "\""); .tell("Valid options: " + ((o.slice(1)).to_english())); continue; } opts = opts.add((opt[1]).to_symbol(), [opt[3], opt[4]]); } } return opts; }; protected method .program_cmd() { arg cmdstr, com, args, @more; var ref, o, i, ops, ign, ed, fl, meth, ex, acc, warn, errs, code, line, errs, code; (> .perms(caller(), 'command) <); ops = args[3]; ref = args[1]; // verify what we have is correct if (!(meth = (| tosym(ref[4]) |))) { ign++; .tell(("The method name '" + (((ref[4]) == 0) ? "" : (ref[4]))) + "' is not acceptable."); } if ((!ign) && ((ref[3]) && (!((ref[3]).is_writable_by(this()))))) { ign++; .tell(("You cannot program on " + ((ref[3]).namef('ref))) + "."); } if ((!ign) && ((| (ref[3]).find_method(meth) |) == (ref[3]))) ex++; // ok, go on with options o = ops.slice(1); if ((i = "e?dited" in o)) { if (!((ops[i])[3])) { if (!($sys.is_admin(this()))) { ign++; .tell("Only admins can shut off edited comments."); } } else { ed = 1; } } else { ed = 1; } if (ed) { ed = (("// $#Edited: " + ($time.format("%d %h %y %H:%M"))) + " ") + this(); if (i && ((ops[i])[4])) ed += ": " + ((ops[i])[4]); } if ((i = "f?lags" in o)) fl = $parse_lib.parse_method_flags((ops[i])[4]); else if (ex) fl = (ref[3]).method_flags(meth); else fl = []; if ((i = "a?ccess" in o)) acc = $parse_lib.parse_method_access((ops[i])[4]); else if (ex) acc = (ref[3]).method_access(meth); else acc = 'public; if ((i = "w?arnings" in o)) warn = (ops[i])[4]; else warn = 1; // now get on with it already if (ign) line = "Ignoring input until \".\" or \"@abort\""; else if (ex) line = ((((("Reprogramming " + acc) + " method ") + (ref[3])) + ".") + meth) + "()"; else line = ((((("Programming " + acc) + " method ") + (ref[3])) + ".") + meth) + "()"; if (fl) line += (" [" + (fl.to_english())) + "]"; code = more ? (more.subrange(2)) : (.read(("-- " + line) + " --")); if (type(code) == 'symbol) { switch (code) { case 'aborted: return; case 'engaged: return "Sorry, you are already reading on this connection."; default: return "Unknown response from the read parser: " + code; } } if (ign) return "Finished ignoring input."; if (ed) code += [ed]; catch any { if ((errs = (ref[3]).add_method(code, meth))) return errs; (> (ref[3]).set_method_flags(meth, fl) <); (> (ref[3]).set_method_access(meth, acc) <); if ((line = (> $code_lib.verify_code(code, meth, warn) <))) .tell(line); return ((((("Method " + (ref[3])) + ".") + meth) + "() ") + (ex ? "re" : "")) + "compiled"; } with { return (traceback()[1])[2]; } }; protected method .rehash_cmd() { arg cmdstr, cmd; var c, o, p; (> .perms(caller(), 'command) <); .tell("Rehashing your commands..."); .purge_caches(); for p in (parents()) p.cache_uninit(); for cmd in (.local_commands()) { for c in (cmd[2]) .add_to_local_cache(c[1]); } for p in (parents()) p.cache_init(); .tell("Done."); }; protected method .show_cmd() { arg cmdstr, com, args; var show, match, i, chop, f, obj, out; (> .perms(caller(), 'command) <); if (((args[1])[1]) == 'object) show = ['method, 'variable]; else if ((args[1])[5]) show = [(args[1])[1], (args[1])[5]]; else show = [(args[1])[1]]; if ((i = "c?hop" in ((args[3]).slice(1)))) chop = ((args[3])[i])[3]; else chop = 1; if ((args[1])[4]) f = (args[1])[4]; else f = .get_setting("match-default", $programmer); match = .get_setting("match-with", $programmer); obj = (args[1])[3]; if (type(obj) == 'frob) return ["The target object was a frob. Please use @exam instead."]; .tell([((("Object: " + obj) + " [") + ((obj.size()).to_english())) + " bytes]", "Parents: " + ((obj.parents()).join(", "))]); if ('method in show) { if (!(obj.has_flag('methods, this()))) .tell(" ** No permission to list methods **"); else .tell(._show_methods(obj, f, match, chop)); } if ('variable in show) { if (!(obj.has_flag('variables, this()))) .tell(" ** No permission to show variables **"); else .tell(._show_variables(obj, f, match, chop)); } .tell("---"); }; public method .spawn_cmd() { arg cmdstr, cmd, args; var match, name, parents, p, line, set, nprog, new, t; (> .perms(caller(), 'command) <); if ((match = match_template(args, "* named *"))) { name = match[3]; args = explode(match[1]); } else { args = args.explode_quoted(); if (!args) return "Spawn from nothing?"; name = ""; } // programmers get additional privs nprog = !(.is($programmer)); parents = []; for p in (args) { catch any { p = (> .match_env_nice(p) <); } with { .tell((traceback()[1])[2]); continue; } if ((!(p.is($thing))) && nprog) { .tell((p.namef('ref)) + " is not a VR object, you may only spawn VR objects."); continue; } if (!(p.has_flag('fertile))) { .tell((p.namef('ref)) + " is not a fertile object."); continue; } parents += [p]; } parents = parents.compress(); if (!parents) return "No capable parents."; if (name) { catch any name = (> $code_lib.parse_name(name) <); with return (traceback()[1])[2]; } // spawn from the first parent, add the others catch any { new = (> (parents[1]).spawn() <); (> new.chparents(@parents) <); // let the user know whats up .tell(((("Spawned new object " + (new.namef('ref))) + " from ") + (map p in (parents) to (p.namef('ref)).to_english())) + "."); if (new.is($located)) (> new.move_to(this()) <); if (name && ((((name[1])[1])[1]) == "$")) { name = (> tosym(((name[1])[1]).subrange(2)) <); (> new.set_objname(name) <); return ("Object name changed to: " + new) + "."; } else if (name) { if (!(new.has_ancestor($has_name))) return new + " cannot be given a VR name."; (> new.set_name(@name[1]) <); for t in (name[2]) (> new.add_name_template(t) <); return (((("Renamed " + new) + " to \"") + (new.name())) + "\"") + ((new.name_templates()) ? ((" (" + ((new.name_templates()).to_english())) + ")") : ""); } } with { .tell((traceback()[1])[2]); if (valid(new)) { line = new.namef('xref); new.destroy(); if (valid(new)) return ("Unable to destroy new object " + line) + "."; else return ("Sucessfully destroyed new object " + line) + "."; } } }; protected method .trace_method_cmd() { arg cmdstr, cmd, ref; var method, current, trace, syn, minfo, line, anc, len, out, m; (> .perms(caller(), 'command) <); if ((ref[1]) != 'method) return toliteral(cmd) + " requires a full method reference."; catch any { method = (> tosym(ref[4]) <); current = (> (ref[2]).find_method(method) <); trace = []; while (current) { trace += [current]; current = (| (ref[2]).find_next_method(method, current) |); } } with { if (error() == ~symbol) return ("Invalid method name \"" + (ref[4])) + "\"."; return (traceback()[1])[2]; } .tell(((("Method trace of " + (ref[2])) + ".") + (ref[4])) + "():"); len = .linelen(); out = []; for anc in (trace.reverse()) { m = anc.method_info(method); out += [strfmt("%5l %4r %l.%l(%l)", $object_lib.parse_method_flags(m[6]), m[4], anc, method, m[1])]; } return out; }; public method .undefine_setting_cmd() { arg cmdstr, cmd, args; var name, definer, syn, name, m; syn = ("Syntax: `" + cmd) + " <definer>:<setting>`"; if (!(m = regexp(args, "^ *([^:]+):([^ $]+)"))) return syn; [definer, name] = m; catch any definer = (> .match_environment(definer) <); with return (traceback()[1])[2]; if (!name) return syn; catch any (> definer.undefine_setting(name) <); with return (traceback()[1])[2]; return ((("Undefined setting " + definer) + ":") + name) + "."; }; protected method .which_cmd() { arg cmdstr, command, str; var m, c, l, t, p, s, cmds, def, out, dname, line, lcache, rcache; (> .perms(caller(), 'command) <); if (!str) return ("Syntax: `" + command) + " <partial or full template>`"; m = #[]; t = (str.explode())[1]; for p in (.ancestors()) { if (p == $has_commands) break; cmds = ._which_cmd(str, p, 'local_commands, " "); cmds += ._which_cmd(str, p, 'remote_commands, "*"); if (cmds) m = m.add(p, cmds); } if (!m) return ("No commands found matching the template \"" + str) + "\"."; l = (.linelen()) / 2; out = [("Commands matching the template \"" + str) + "\":"]; lcache = .local_cache(); rcache = .remote_cache(); for def in (m) { dname = (" " + (def[1])) + "."; for c in (def[2]) { line = ((((c[1]) + ((c[2]).pad(l))) + dname) + tostr(c[3])) + "()"; s = (((((c[2]).explode())[1]).strip("?")).explode("|"))[1]; if ((| lcache[s] |) || (| rcache[s] |)) line = " " + line; else line = "!" + line; out += [line]; } } return out; };