/
MinimalCore-3.2/
MinimalCore-3.2/src/
new object $network: $root;

var $root inited = 1;

public method .bind_port() {
    arg port;
    
    return (> bind_port(port) <);
};


new object $daemon: $network;

var $root inited = 1;
var $daemon default_port = 1138;
var $daemon connection = 0;
var $daemon next_connection = 0;
var $daemon port = 0;

public method .stop_listening() {
    arg [port];
    
    if (connection)
        connection.daemon_shutdown();
    next_connection = 0;
};

public method .start_listening() {
    arg [port];
    
    (| .stop_listening() |);
    port = [@port, default_port][1];
    next_connection = connection.new();
    (> next_connection.bind_port(port) <);
};

public method .new_connection() {
    var new_connection;
    
    if (!valid(next_connection))
        next_connection = connection.new();
    new_connection = next_connection;
    
    // This should fork, once the driver will allow it
    new_connection.start();
    
    // bind to the next connection
    next_connection = connection.new();
    (> next_connection.bind_port(port) <);
};

public method .startup() {
    arg [args];
    var name, opt;
    
    catch any {
        name = tostr(.objname('symbol));
        name = "p" + (name.subrange(1, ("_" in name) - 1));
        args = $parse.getopt(args.to_string(), [[name, 1]]);
        opt = name in ((args[2]).slice(1));
        port = (| toint((args[opt])[4]) |) || default_port;
        (| .stop_listening() |);
        (> .start_listening(port) <);
        .log(((("** Starting " + (.objname())) + " on port ") + tostr(port)) + " **");
    } with {
        switch (error()) {
            case ~bind:
                .log(("** Unable to bind to port " + tostr(port)) + "! **");
            default:
                .log($parse.traceback(traceback()));
        }
    }
};

public method .port() {
    return port;
};

public method .shutdown() {
    arg [args];
    
    (> .stop_listening() <);
};


new object $login_daemon: $daemon;

var $root inited = 1;
var $daemon default_port = 1138;
var $daemon connection = $login_connection;
var $daemon next_connection = 0;
var $daemon port = 0;


new object $connection: $network;

var $root inited = 1;
var $connection buffer = `[];
var $connection host = "";
var $connection daemon = 0;
var $connection active = 0;
var $connection line_buffer = 0;
var $connection interface = 0;
var $connection read_block = [];
var $connection started_at = 0;
var $connection port = 0;

root method .init_connection() {
    buffer = `[];
    host = "";
    line_buffer = [];
};

root method .uninit_connection() {
    (| close_connection() |);
    active = 0;
    if (interface)
        (| interface.connection_going_away(.address(), daemon.port()) |);
    interface = 0;
};

private method .set_host() {
    arg host;
    
    set_var('host, host);
};

private method .new_interface() {
    arg interface;
    
    set_var('interface, interface);
};

private method .set_daemon() {
    arg daemon;
    
    set_var('daemon, daemon);
};

public method .new() {
    var child, port, i;
    
    child = .spawn();
    port = sender().port();
    child.set_daemon(sender());
    child.set_port(port);
    child.new_interface(interface.new(child));
    return child;
};

public method .daemon_shutdown() {
    var c;
    
    for c in (.children())
        (> c.close() <);
};

public method .address() {
    return host;
};

public method .change_interface() {
    arg new;
    var old;
    
    if (interface) {
        old = interface;
        old.connection_going_away(.address(), port);
    }
    interface = new;
    interface.connection_starting(.address(), port);
};

public method .write() {
    arg what, [how];
    var elem, sep;
    
    sep = ('non_terminated in how) ? `[] | `[10];
    switch (type(what)) {
        case 'string:
            what = $buffer.from_strings([what], sep);
        case 'list:
            what = $buffer.from_strings(what, sep);
        case 'buffer:
        default:
            throw(~type, "Write: strings, list of strings and buffers.");
    }
    cwrite(what);
};

public method .parse() {
    arg incoming;
    var lines, line, index;
    
    lines = (buffer + incoming).to_strings();
    index = lines.length();
    buffer = lines[index];
    lines = lines.delete(index);
    line_buffer = [@line_buffer, @lines];
    while (line_buffer) {
        line = line_buffer[1];
        line_buffer = line_buffer.delete(1);
        (| .parse_line(line) |);
    }
};

private method .parse_line() {
    arg line;
    
    if (read_block) {
        read_block = read_block.parse(line);
        line = .rehash_read_status();
        if ((!line) && (line != ""))
            return;
    }
    if ((interface.parse_line(line)) == 'disconnect)
        (> .close() <);
};

public method .cwritef() {
    arg fname;
    
    (> cwritef(fname) <);
};

public method .is_reading_block() {
    return read_block ? 1 | 0;
};

public method .finish_reading_block() {
    var task_id, lines;
    
    task_id = read_block.task_id();
    lines = read_block.lines();
    read_block = 0;
    resume(task_id, lines);
};

public method .abort_reading_block() {
    read_block = read_block.add('lines, 'aborted);
    .finish_reading_block();
};

public method .start_reading_block() {
    arg count;
    
    read_block = $read_parser.new(task_id(), count);
    return suspend();
};

private method .rehash_read_status() {
    switch (read_block.status()) {
        case 'abort:
            .abort_reading_block();
        case 'not_done:
            // do nothing
        case 'done:
            .finish_reading_block();
        case 'pass_command:
            return read_block.command();
    }
    return 0;
};

public method .close() {
    (> .destroy() <);
};

driver method .disconnect() {
    arg [args];
    
    (| .close() |);
};

private method .set_port() {
    arg port;
    
    set_var('port, port);
};

public method .active() {
    return active;
};

driver method .connect() {
    arg host, socket;
    
    set_var('host, host);
    daemon.new_connection();
};

public method .start(): fork {
    active = time();
    interface.connection_starting(host, port);
};

public method .interface() {
    return interface;
};


new object $login_connection: $connection;

var $root inited = 1;
var $connection interface = $login_interface;


new object $connection_interface: $network;

var $root inited = 1;
var $connection_interface connection = 0;
var $connection_interface controller = 0;
var $connection_interface commands = 0;

public method .parse_line() {
    arg line;
    var cmd, c, match, parsed, i, m, a, u;
    
    catch any {
        while (line && ((line[1]) == " "))
            line = line.subrange(2);
        if (!line) {
            return .null_cmd(line);
        } else {
            c = controller.match_command(line);
            return (> .(c[1])(@c.subrange(2)) <);
        }
    } with {
        if (error() != ~stop) {
            .print($parse.traceback(traceback()));
            return 'disconnect;
        }
    }
};

public method .commands() {
    return commands;
};

public method .match_command() {
    arg line;
    var cmd, c, match;
    
    if (!commands)
        return ['invalid_cmd];
    for c in [1 .. commands.length()] {
        match = ((commands[c])[1]).match_template(line);
        if (match)
            return [(commands[c])[2], @match];
    }
    return ['invalid_cmd];
};

public method .connection_going_away() {
    arg [args];
    
    (| .destroy() |);
};

public method .print() {
    arg what;
    
    return (> connection.write(what) <);
};

protected method .null_cmd() {
    arg [args];
    
    return 'disconnect;
};

protected method .invalid_cmd() {
    arg [args];
    
    return 'disconnect;
};

public method .connection() {
    return connection;
};

public method .new() {
    arg c;
    var i;
    
    i = .spawn();
    i.set_connection(c);
    i.set_controller(this());
    return i;
};

public method .new_connection() {
    arg host, port;
    
};

private method .set_connection() {
    arg c;
    
    connection = c;
};

protected method .controller() {
    return controller;
};

private method .set_controller() {
    arg c;
    
    controller = c;
};

public method .daemon_shutdown() {
    var i;
    
    for i in (.children())
        (| i.destroy() |);
};

public method .connection_starting() {
    arg host, port;
    
    .print(["", ("** Minimal Core Mark I online, port " + tostr(port)) + " **", ""]);
};


new object $login_interface: $connection_interface;

var $root inited = 1;
var $connection_interface commands = [["QUIT", 'quit_cmd]];

protected method .null_cmd() {
    arg [args];
    
    .invalid_cmd();
};

protected method .invalid_cmd() {
    arg [args];
    
    .print("Commands: " + ((((.controller()).commands()).slice(1)).to_english()));
};

protected method .quit_cmd() {
    arg [args];
    
    .print("Goodbye.");
    return 'disconnect;
};