Command-handling object

    This object defines command-handling behavior.

    Public methods:

	match_command(str)			Match command against str
	commands()				Get list of commands
	all_commands()				Get commands on ancestors too

    Owner methods:

	add_command(template, type, method)	Add a command
	remove_command(method)			Remove a command

parent root
object commands

var commands commands 0

method init
    arg ancestors;

    (> pass(ancestors) <);
    if (definer() in ancestors)
	commands = [];
.

eval
    .initialize();
    .set_name("Generic command-handling object");
.

method add_command
    arg template, type, method;

    if (!.is_owned_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.");
    if (type != 'template && type != 'pattern)
	throw(~type, "Type is neither 'template nor 'pattern.");
    commands = commands + [[template, type, method]];
.

method del_command
    arg method;
    var command;

    if (!.is_owned_by(sender()))
	throw(~perm, "Sender is not an owner.");
    for command in (commands) {
	if (command[3] == method) {
	    commands = setremove(commands, command);
	    return;
	}
    }
    throw(~commandnf, "No command with method " + tostr(method));
.

method match_command
    arg str;
    var cmd, fields;

    for cmd in (.all_commands()) {
	if (cmd[2] == 'template)
	    fields = match_template(cmd[1], str);
	else
	    fields = match_pattern(cmd[1], str);
	if (fields)
	    return [cmd[3], fields];
    }
    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 == $commands)
	    break;
    }
    return cmdlist;
.