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

parent $root
object $network

var $root objname 'network
var $root inited 1

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

parent $network
object $daemon

var $root objname 'daemon
var $root inited 1
var $daemon default_port 1138
var $daemon connection 0
var $daemon next_connection 0
var $daemon port 0

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

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

public 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 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 port
    return port;
.

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

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

parent $network
object $connection

var $root objname 'connection
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 init_connection
    buffer = `[];
    host = "";
    line_buffer = [];
.

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

private set_host
    arg host;
    
    set_var('host, host);
.

private new_interface
    arg interface;
    
    set_var('interface, interface);
.

private set_daemon
    arg daemon;
    
    set_var('daemon, daemon);
.

public 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 daemon_shutdown
    var c;
    
    for c in (.children())
        (> c.close() <);
.

public address
    return host;
.

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

public 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 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 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 echo_file
    arg fname;
    
    (> echo_file(fname) <);
.

public is_reading_block
    return read_block ? 1 | 0;
.

public 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 abort_reading_block
    read_block = read_block.add('lines, 'aborted);
    .finish_reading_block();
.

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

private 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 close
    (> .destroy() <);
.

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

private set_port
    arg port;
    
    set_var('port, port);
.

public active
    return active;
.

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

public start
    // Make this method 'fork' from the regular thread stack
    active = time();
    interface.connection_starting(host, port);
.

public interface
    return interface;
.

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

parent $network
object $connection_interface

var $root objname 'connection_interface
var $root inited 1
var $connection_interface connection 0
var $connection_interface controller 0
var $connection_interface commands 0

public 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 commands
    return commands;
.

public 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 connection_going_away
    arg [args];
    
    (| .destroy() |);
.

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

protected null_cmd
    arg [args];
    
    return 'disconnect;
.

protected invalid_cmd
    arg [args];
    
    return 'disconnect;
.

public connection
    return connection;
.

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

public new_connection
    arg host, port;
    
.

private set_connection
    arg c;
    
    connection = c;
.

protected controller
    return controller;
.

private set_controller
    arg c;
    
    controller = c;
.

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

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

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

parent $daemon
object $login_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


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

parent $connection_interface
object $login_interface

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

protected null_cmd
    arg [args];

    .invalid_cmd();
.
protected invalid_cmd
    arg [args];

    .print("Commands: " + .controller().commands().slice(1).to_english());
.

protected quit_cmd
    arg [args];

    .print("Goodbye.");
    return 'disconnect;
.

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

parent $connection
object $login_connection

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