/
MinimalCore-2.0/
MinimalCore-2.0/src/
// ------------------------------------------------------------------
// $network
//
// This object functions as a placeholder.

new object $network: $root;

var $root objname = 'network;
var $root inited = 1;

// ------------------------------------------------------------------
// $daemon

new object $daemon: $network;

var $root objname = 'daemon;
var $root inited = 1;

var default_port = 1138;
var connection = 0;
var next_connection = 0;
var 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();
    (> bind_port(port, next_connection) <);
};

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();

    (> bind_port(port, next_connection) <);
};

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 handler {
        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() <);
};

// ------------------------------------------------------------------
// $connection

new object $connection: $network;

var $root objname = 'connection;
var $root inited = 1;

var buffer = `[];
var host = "";
var daemon = 0;
var active = 0;
var line_buffer = 0;
var interface = 0;
var read_block = [];
var started_at = 0;
var 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.");
    }
    echo(what);
};

public method parse {
    arg incoming;
    var lines, line, index;
    
    lines = buffer.append(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 echo_file {
    arg fname;
    
    (> echo_file(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 $connection_interface: $network;

var $root objname = 'connection_interface;
var $root inited = 1;

var connection = 0;
var controller = 0;
var 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 handler {
        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_daemon: $daemon;

var $root objname = 'login_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 $login_interface: $connection_interface;

var $root objname = 'login_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;
};


// ------------------------------------------------------------------

new object $login_connection: $connection;

var $root objname = 'login_connection;
var $root inited = 1;
var $connection interface = $login_interface;