/
CDC-1.2b/
CDC-1.2b/src/
parent $old_command_environment
object $has_commands

var $has_commands commands []
var $has_commands shortcuts []
var $root child_index 2
var $root owners [$has_commands]
var $root fertile 0
var $root inited 1
var $root owned [$has_commands]
var $root manager $has_commands
var $root writable [$has_commands]
var $root readable ['parameters, 'methods, 'code]
var $ title "untitled"
var $ brief ""
var $ help_name ""
var $ text []
var $ footnotes []
var $ references #[]
var $ upnode $
var $ subnodes []
var $ menu 0
var $root dbref 'has_commands

method init_has_commands
    if (caller() != $root)
        throw(~perm, "Caller is not $root.");
    commands = [];
    shortcuts = [];
.

method uninit_has_commands
    if (caller() != $root)
        throw(~perm, "Caller is not $root.");
    commands = 0;
    shortcuts = 0;
.

method add_command
    arg template, method;
    
    if (!(.is_writable_by(sender())))
        throw(~perm, "Sender is not an owner.");
    if ((type(template) != 'string) || (type(method) != 'symbol))
        throw(~type, "Template and method are not a string and symbol.");
    commands = [@commands, [template, method]];
.

method del_command
    arg match;
    var command, x;
    
    .perms(sender());
    
    // so you can do both template or method
    if (type(match) == 'symbol)
        x = 2;
    else if (type(match) == 'string)
        x = 1;
    else
        throw(~type, "Type must either be a string or symbol (template or method).");
    for command in (commands) {
        if ((command[x]) == match) {
            commands = setremove(commands, command);
            return;
        }
    }
    throw(~commandnf, "No command with match " + toliteral(match));
.

method add_shortcut
    arg pattern, method, subs;
    
    if (!(.is_writable_by(sender())))
        throw(~perm, "Sender is not an owner.");
    if ((type(pattern) != 'string) || ((type(method) != 'symbol) || (type(subs) != 'list)))
        throw(~type, "Pattern, method, and subs are not a string, symbol, and list.");
    shortcuts = [@shortcuts, [pattern, method, subs]];
.

method del_shortcut
    arg method;
    var shortcut;
    
    if (!(.is_writable_by(sender())))
        throw(~perm, "Sender is not an owner.");
    for shortcut in (shortcuts) {
        if ((shortcut[2]) == method) {
            shortcuts = setremove(shortcuts, shortcut);
            return;
        }
    }
    throw(~shortcutnf, "No shortcut with method " + tostr(method));
.

method match_command
    arg str;
    var shortcut, cmd, fields, shortcuts, commands;
    
    // Try shortcuts.
    shortcuts = .xshortcuts_cache();
    for shortcut in (shortcuts) {
        fields = match_pattern(shortcut[1], str);
        if (fields)
            return [shortcut[2], .sub_shortcut_fields(shortcut[3], fields)];
    }
    
    // Try commands.
    commands = .xcommand_cache();
    for cmd in (commands) {
        fields = match_template(cmd[1], str);
        if (fields)
            return [cmd[2], fields];
    }
    
    // Give up.
    return 0;
.

method commands
    return commands;
.

method all_commands
    var p, cmdlist, pc;
    
    // Collect complete command list from ancestors.  Ancestors may not be
    // command-handling objects, in which case (| p.commands() |) is
    // ~methodnf.
    cmdlist = [];
    for p in (ancestors()) {
        pc = (| p.commands() |);
        if (pc)
            cmdlist = cmdlist + pc;
        if (p == definer())
            break;
    }
    return cmdlist;
.

method shortcuts
    return shortcuts;
.

method all_shortcuts
    var ancestor, list, ancestor_shortcuts;
    
    // Collect complete command list from ancestors.  Ancestors may not be
    // command-handling objects, in which case (| p.shortcuts() |) is
    // ~methodnf.
    list = [];
    for ancestor in (ancestors()) {
        ancestor_shortcuts = (| ancestor.shortcuts() |);
        if (ancestor_shortcuts)
            list = list + ancestor_shortcuts;
        if (ancestor == definer())
            break;
    }
    return list;
.

method sub_shortcut_fields
    arg subs, fields;
    var subbed_list, elem;
    
    subbed_list = [];
    for elem in (subs) {
        if (type(elem) == 'string)
            subbed_list = [@subbed_list, elem];
        else if (type(elem) == 'integer)
            subbed_list = [@subbed_list, (> fields[elem] <)];
        else
            throw(~type, "Substitution element is of wrong type.");
    }
    return subbed_list;
.

method command_info
    arg t;
    var x, y, out;
    
    // eventually will change commands to use dictionaries.
    out = [];
    for x in (commands) {
        y = ((x[1]).explode())[1];
        if (match_template(y, t))
            out = [@out, x];
    }
    return out;
.