/
CDC-1.2b/
CDC-1.2b/src/
parent $user_interfaces
object $help_interface

var $root child_index 1
var $root owners [$]
var $root owned [$help_interface]
var $root fertile 1
var $has_commands commands []
var $has_commands shortcuts []
var $root inited 1
var $help_interface help_dict #[["*", $], ["help", $], ["**", $]]
var $help_interface current_help $
var $root manager $help_interface
var $root writable [$help_interface]
var $root readable ['parameters, 'methods, 'code]
var $root dbref 'help_interface
var $old_command_environment verb_cache #[]
var $old_command_environment command_cache []
var $old_command_environment shortcuts_cache []

method init_help
    if (caller() != $root)
        throw(~perm, "Caller is not $root.");
    current_help = $help_root;
    help_dict = #[["*", $help_root], ["help", $help_help], ["**", $user_help]];
.

method help_dict
    return help_dict;
.

method match_help_path
    arg string;
    var current, node;
    
    current = current_help;
    for node in (explode(string))
        current = (> .match_help_node(node, current) <);
    return current;
.

method match_help_node
    arg string, current;
    var node, sib, i;
    
    switch (string) {
        case ">":
            node = current;
            while (1) {
                if (node == $help_root)
                    throw(~keynf, (current.help_name()) + " has no successor.");
                sib = (node.upnode()).subnodes();
                i = node in sib;
                if (i && (i < listlen(sib)))
                    return sib[i + 1];
                node = node.upnode();
            }
        case "<":
            node = current;
            while (1) {
                if (node == $help_root)
                    throw(~keynf, (current.help_name()) + " has no predecessor.");
                sib = (node.upnode()).subnodes();
                i = node in sib;
                if (i > 1)
                    return sib[i - 1];
                node = node.upnode();
            }
        case "..":
            if (current == $help_root)
                throw(~keynf, "Top node has no supernode.");
            return current.upnode();
        case "?":
            if (!(current.subnodes()))
                return (> .match_help_node(">", current) <);
            return (current.subnodes())[1];
    }
    catch ~keynf {
        return help_dict[string];
    }
    return (> current.match_menu(string) <);
.

method help_cmd
    arg string, [rest];
    var command, matches, method;
    
    if (rest)
        string = rest[1];
    else if (string == "help")
        string = "";
    
    // if an extended command is present, put it in command, and the rest
    // of the line back into string
    command = "read";
    if (string && ((string[1]) == "-")) {
        matches = match_regexp("^-([a-z]+)( +(.*))?", string);
        if (!matches) {
            sender().tell("invalid help syntax");
            return;
        }
        command = substr(string, (matches[2])[1], (matches[2])[2]);
        if ((matches[4])[1])
            string = substr(string, (matches[4])[1], (matches[4])[2]);
        else
            string = "";
    }
    
    // now call the appropriate handler
    method = tosym(command + "_helpcmd");
    catch ~methodnf {
        .(method)(sender(), string);
    } with handler {
        sender().tell(("\"" + command) + "\" is not a valid help subcommand.");
    }
    return;
.

method destroy_helpcmd
    arg source, string;
    
    // deletes the current node
    if (string) {
        source.tell("Usage: help -destroy");
    } else if (current_help == $help_root) {
        source.tell("cannot destroy the root node");
    } else {
        current_help = current_help.upnode();
        current_help.destroy();
    }
.

method relink_helpcmd
    arg source, string;
    var target, path;
    
    // relink a node to another location
    // usage: help -relink <path>"
    if (!string) {
        source.tell("Usage: help -relink <path>");
        return;
    } else if (current_help == $help_root) {
        source.tell("Can't move the root node.");
        return;
    }
    
    // lookup the target node
    catch ~keynf {
        target = .match_help_path(string);
    } with handler {
        source.tell(("No help found on " + toliteral(string)) + ".");
        return;
    }
    
    // check for loops
    if (current_help == target) {
        source.tell("Can't link a node to itself.");
    } else if (current_help in (target.path())) {
        source.tell("Requested move would create a loop.");
    } else {
        // move the node
        current_help.set_upnode(target);
        path = [@current_help.path(), current_help];
        path = "Help path: " + (path ? $list.to_string($list.map(path, 'help_name), " ") | "<none>");
        source.tell(path);
    }
.

method add_helpcmd
    arg source, string;
    var matches, what, name, help_name, brief, text;
    
    //
    // add a new node
    // called by: $help.help_cmd
    //
    // usage: help -add name help-name brief < note-object
    //        help -add name help-name brief : help-name <<
    //
    // the first form takes the text from a note or similar object
    // the second form reads the text from the keyboard
    //
    // check the syntax
    matches = match_regexp("([^ ]+) (.*) < (.*)", string);
    if (!matches)
        matches = match_regexp("([^ ]+) (.*) <<", string);
    if (!matches) {
        source.tell("Invalid syntax for add subcommand.  Please use one of:");
        source.tell("  help -add help-name brief < note-object");
        source.tell("  help -add help-name brief <<");
        return;
    }
    
    // parse the match results
    name = substr(string, (matches[2])[1], (matches[2])[2]);
    brief = substr(string, (matches[3])[1], (matches[3])[2]);
    if ((matches[4])[1])
        what = substr(string, (matches[4])[1], (matches[4])[2]);
    else
        what = "";
    
    // see if we are getting text from keyboard or a note
    if (what) {
        // reading from note: get the text
        catch any {
            what = source.match_environment(what);
            text = what.text();
        } with handler {
            source.tell("Couldn't get text from object " + what);
            return;
        }
    
        // create a subnode
        what = current_help.create_subnode(name, brief, text);
        source.tell(((("Created new help node " + (what.help_name())) + " [") + (what.brief())) + "]");
    } else {
        // reading from keyboard: read the text
        source.tell("keyboard input not implemented yet");
    }
.

method read_helpcmd
    arg source, string;
    
    // check for footnote access
    catch ~range {
        source.tell((("[:" + string) + ":] ") + (current_help.footnote(toint(string))));
        return;
    }
    
    // lookup the node to be read
    catch ~keynf {
        current_help = .match_help_path(string);
    } with handler {
        source.tell(("No help found on " + toliteral(string)) + ".");
        return;
    }
    
    // dump the contents of the node
    source.tell(current_help.text());
    
    // tell the user what the next node is, if any
    if (current_help.subnodes()) {
        source.tell(((("-----[ `??' to read " + (((current_help.subnodes())[1]).brief())) + " (") + (((current_help.subnodes())[1]).help_name())) + ") ]");
    } else {
        catch ~keynf {
            string = .match_help_node("?", current_help);
            source.tell(((((("-----[ `??' to read " + (string.brief())) + " (") + ($list.to_string($list.map(string.path(), 'help_name), " "))) + " ") + (string.help_name())) + ") ]");
        } with handler {
            source.tell("-----[ Last node. `?*' to return to the top. ]");
        }
    }
.

method text_helpcmd
    arg source, string;
    var what, text;
    
    // change the text of a help node
    // usage: help -text note
    //        help -text <<
    if (!string) {
        source.tell("Usage: help -text note");
    } else if (string == "<<") {
        source.tell("Keyboard input not implemented yet.");
    } else {
        // parse the object containing the text
        catch any {
            what = source.match_environment(string);
            text = what.text();
        } with handler {
            source.tell("Couldn't get text from object " + string);
            return;
        }
    
        // install text
        current_help.set_text(text);
        source.tell("Updated text.");
    }
.

method name_helpcmd
    arg source, string;
    var exp, name, brief;
    
    // rename the node -- usage: help -name name brief
    if (!string) {
        source.tell("Usage: help -name <name> <brief>");
        return;
    }
    
    // parse the arguments
    exp = explode(string);
    if (listlen(exp) < 2)
        source.tell("Usage: help -name <name> <brief>");
    name = exp[1];
    brief = substr(string, strlen(name) + 2);
    
    // set the names
    current_help.set_help_name(name);
    current_help.set_brief(brief);
    
    // tell the user what happened
    source.tell(((("node is now: " + (current_help.help_name())) + " [") + (current_help.brief())) + "]");
.

method reset_help
    arg [rest];
    
    if (!(.is_writable_by(sender())))
        throw(~perm, "Not an owner");
    current_help = $help_root;
.

method init_help_interface
    if (caller() != $root)
        throw(~perm, "Caller is not $root.");
    current_help = $help_root;
    help_dict = #[["*", $help_root], ["help", $help_help], ["**", $user_help]];
.

method init_help_interface_old
    if (caller() != $root)
        throw(~perm, "Caller is not $root.");
    current_help = $help_root;
    help_dict = #[["*", $help_root], ["help", $help_help], ["**", $user_help]];
.