// ------------------------------------------------------------------ // $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